C++基础

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

目录

在Ubuntu 下编写C++

在Ubuntu 上面编写C++本章节内容主要介绍在Ubuntu 在终端窗口下使用vi/vim 编辑一
个C++源文件。通过编写最简单的示例“Hello,World”。带领大家学习如何在Ubuntu 终端下编辑和编译C++。这里要求大家会在Ubuntu 上使用vi/vim也就是要求大家有一点Ubuntu 入门的基础。如果没有这些基础也是可以拷贝C++的代码到Windows 上使用像Dev-C++这种轻量级C/C++ 集成开发环境IDE进行编写和编译。

但是笔者还是希望大家和笔者一起学习在Ubuntu 下编写C++因为后面第二章的内容都
是在Ubuntu 下编写和讲解C++的基础。同时也可以锻炼在Linux 开发C++的能力

C++简介

C++ c plus plus是一种静态类型的、编译式的、通用的、大小写敏感的、不规则的编程
语言支持过程化编程、面向对象编程和泛型编程。C++ 被认为是一种中级语言它综合了高级语言和低级语言的特点。C++ 是由Bjarne Stroustrup 于1979 年在新泽西州美利山贝尔实验室开始设计开发的。C++ 进一步扩充和完善了C 语言最初命名为带类的C后来在1983 年更名为C++。C++ 是C 的一个超集事实上任何合法的C 程序都是合法的C++ 程序。
截止2020 年在2017 年发布C++17已经是第五个C++标准了。我们也见过或者听过C++98这样的C++标准也就是1998 年发布的C++所以叫C++98是C++的第一个标准。

学习C++我们要理解概念而不是深究语言技术细节。我们只要带着第二章的C++基础概
念学习Qt 或者写C++会有一定的帮助。

C++环境设置

为了写这份教程作者也是从新装了一个Ubuntu18.04。从头搭建环境。我们先配置软件源
的服务器地址为阿里云的地址。这样我们可以从国内去获取软件源下载速度会更快。
我们要在Ubuntu 编写C++程序那么需要有能编写代码的文本编辑和C++编译器。在新
装的Ubuntu 环境里编译C 语言的GCC 没有安装编译C++的G++也没有安装。执行下面的执指令安装编译C 语言和C++的环境。

sudo apt-get install gcc g++
sudo apt-get install lsb-core lib32stdc++6 // 安装其他库

安装完成后可以使用下面的指令来查看安装的gcc 和g++的版本。

g++ -v
gcc -v

在这里插入图片描述

编写一个简单的C++程序

在终端输入下面的指令首先我们创建一个C++目录然后使用cd 指令进入C++目录。
再创建01_hello_world 目录进入01_hello_world 目录然后使用vi 指令编辑
01_hello_world.cpp。

mkdir C++ // 创建一个C++目录。
cd C++ // 进入创建的C++目录。
mkdir 01_hello_world // 创建一个01_hello_world 目录
cd 01_hello_world // 进入01_hello_world 目录下。
vi 01_hello_world.cpp // 编辑cpp 文件拷贝下文的内容

在这里插入图片描述
拷贝下面的内容到01_hello_world.cpp。

#include <iostream>
using namespace std;
int main()
{
	cout << "Hello, World!" << endl;
	return(0);
}

第1 行C++ 语言定义了一些头文件这些头文件包含了程序中必需的或有用的信息。上
面这段程序中包含了头文件。
第2 行using namespace std; 告诉编译器使用std 命名空间。命名空间是C++ 中一个相
对新的概念。其中std 就是C++里的标准命名空间也就是标准库里写好的了我们可以直接调用。
第3 行int main() 是主函数程序从这里开始执行。
第5 行cout << “Hello World”<<endl; 会在屏幕上显示消息"Hello World"并换行。“<<”
是运算符endl 是换行语句。
第6 行return 0; 终止main( )函数并向调用进程返回
执行下面的语句进行编译和运行这个简单的C++程序。

g++ 01_hello_world.cpp -o 01_hello_world // 使用g++编译。-o 后面加的是输出的目标文件。
./01_hello_world // 在终端下执行打印"Hello, World!"并换行。

在这里插入图片描述
我们可以拓展一下如何输出多行。可以像下面一样无限加下去。其中我们发现打印了第
一个Hello,world!后也换行了因为使用了“\n”。C++中可以使用C 语言的语句C++是C 语
言的超集。

#include <iostream>
using namespace std;
int main()
{
	cout << "Hello, world!\n" << "Hello, world!" << endl;
	return(0);
}

第5 行我们在里面再加用“<<”插入运算符重载运算符再插入一句“Hello, world!”
打印这样终端上就打印了两行“Hello, world!”。

C++基础

在第二章C++基础里这里主要介绍概念为主主要介绍C++与C 语言中常用的不同点
和一些新的变化。其中不会去说指针、数据类型、变量类型、判断和循环等这些知识这些和C 语言基本是一样使用的。我们主要学习C++的面向对象编程对学习Qt 有很大的帮助理解第2.2 章节的概念很重要。Qt 里就能体现到C++编程带来的优势和便处。就算没学过C++学习Qt 也不会很难。写C++基础这章笔者已经把重要的概念写出来但是实际上C++的内容不止这么多第二章是快餐式C++入门主要是为了更好的理解Qt 中的C++语法学习Qt时也方便理解其中的内容。

C++的新特性

C++比C 语言新增的数据类型是布尔类型bool。但是在新的C 语言标准里已经有布尔类
型了但是在旧的C 语言标准里是没有布尔类型的编译器也无法解释布尔类型。
在传统的C 语言里变量初始化时必须在程序的前面定义在前面而C++则是可以随用随
定义。C++也可以直接初始化比如int x(100);这样就直接赋值x=100这些都是C++特性的好处。这里只说这些常用的新特性其他特性不做描述或者解释了。

C++的输入输出方式

在C 语言里我们是这样输入或者输出的。
在这里插入图片描述
在C++里我们使用以cin 和cout 代替了scanf 和printf。在输入和输出的流程上是不变的
只是关键字变了用法也变了。
在这里插入图片描述
要说效率上肯定是C 语言的scanf 和printf 的效率高但是没有C++中的cin 和cout 使
用方便。
C++的I/O 语法方式如下。
cout 语法形式

cout<<x<<endl;

x 可以是任意数据类型甚至可以写成一个表达式这比C 语言需要指定数据类型方便多
了endl 指的是换行符与C 语言的“\n”效果一样。

错误示例

cout<<x,y<<endl; // 在变量间不能用“”。

正确写法

cout<<x<<y; // endl 可流省略只是一个换行的效果。

cin 语法形式

cin>>x;

x 可以是任意数据类型。
拓展如何输入两个不同的变量。

cin>>x>>y;

C++之命名空间namespace

在第1.3 小节里我们已经使用过命名空间如下代码第2 行。using namespace std;同时我们
要注意第1 行不能写成iostream.h有.h 的是非标准的输入输出流c 的标准库。无.h 的是标准输入输出流就要用命名空间。

#include <iostream>
using namespace std;
int main()
{
	cout << "Hello, World!" << endl;
	return(0);
}

using 是编译指令声明当前命名空间的关键词。可以从字面上理解它的意思using 翻译
成使用。这样可以理解成使用命名空间std。因为cin 和cout 都是属于std 命名空间下的东西所以使用时必须加上using namespace std;这句话。cin 和cout 可以写std::cin 和std::cout“::”表示作用域cin 和cout 是属于std 命名空间下的东西这里可以理解成std 的cin 和std 的cout。
为什么要使用命名空间
有些名字容易冲突所以会使用命名空间的方式进行区分具体来说就是加个前缀。比如
C++ 标准库里面定义了vector 容器您自己也写了个vector 类这样名字就冲突了。于是标
准库里的名字都加上std:: 的前缀您必须用std::vector 来引用。同理您自己的类也可以加
个自定义的前缀。但是经常写全名会很繁琐所以在没有冲突的情况下您可以偷懒写一句
using namespace std;接下去的代码就可以不用写前缀直接写vector 了。
从命名空间开始我们就隐隐约约可以看到C++面向对象的影子了。命名空间在很多C++库
里使用到。有些公司也会自定义自己的C++库里面使用了大量的命名空间。从这里我们也可以看出C++是非常之有条理的容易管理的不含糊易使用的。
在初学Qt 时我们是比较少使用命名空间或者比较少看到命名空间。当然也是可以在Qt
里自定义命名空间然后与C++一样正常使用。

下面通过一个简单的例子来介绍自定义的命名空间和使用自定义的命名空间。在Ubuntu
上我们新建一个目录02_namespace_example然后在02_namespace_example 里新建一个02_namespace_example.cpp 文件内容如下。

#include <iostream>
using namespace std;

namespace A
{
	int x = 1;
	void fun()
	{
		cout << "A namespace" << endl;
	}
}
using namespace A;
int main()
{
	fun();
	A::x = 3;
	cout << A::x << endl;
	A::fun();
	return(0);
}

第4 行自定义了命名空间A里面定义了一个变量x并将x 赋值为1定义了一个函数
fun()并在fun()加了输出打印语句cout<<“A namespace”<<endl;。
第11 行声明使用命名空间A。
第14 行在第11 行声明了命名空间A 后才能直接使用fun();否则要写成A::fun();
第15 行将A 命名空间下的x 重新赋值为3。
第16 行打印出A 命名空间下的x 的值。
第17 行调用A 命名空间下的fun()。
执行下面的指令开始编译。

g++ 02_namespace_example.cpp -o 02_namespace_example

编译完成执行的结果如下。
在这里插入图片描述

C++面向对象

面向对象的三大特征是继承多态和封装C++重面向对象重要的就是这些我们下面通
过一些简单的实例加以理解从这小节开始我们将开启新的编程旅途。与C 语言编程的思想完全不同了这就是C++!理解概念和掌握这些编程方法对学习C++有很大的好处。

类和对象

C++ 在C 语言的基础上增加了面向对象编程C++ 支持面向对象程序设计。类是C++
的核心特性通常被称为用户定义的类型。类用于指定对象的形式它包含了数据表示法和用于处理数据的方法。类中的数据和方法称为类的成员。函数在一个类中被称为类的成员。
打个比方说明一下什么是类比如有一条小狗小狗有名字叫旺财旺财的年龄是2 岁
同时旺财会汪汪的叫也能跑。我们统称狗这个为类类是我们抽象出来的因为狗不只有上面的属性还有体重毛发的颜色等等我们只抽象出几种属性成一个类。具体到哪条狗就叫对象。

从类中实例化对象分两种方法一种是从栈中实例化对象一种是从堆中实例化对象。
下面以自定义狗类介绍如何自定义类和如何使用对象。
在Ubuntu 上编辑一个03_class_dog_example 目录在03_class_dog_example 目录下新建
一个03_class_dog_example.cpp 文件内容如下。

#include <iostream>
#include <string>
using namespace std;

class Dog
{
public:
	string	name;
	int	age;

	void run()
	{
		cout << "小狗的名字是:" << name << "," << "年龄是" << age << endl;
	}
};

int main()
{
	Dog dog1;

	dog1.name	= "旺财";
	dog1.age	= 2;
	dog1.run();

	Dog *dog2 = new Dog();

	if ( NULL == dog2 )
	{
		return(0);
	}
	dog2->name	= "富贵";
	dog2->age	= 1;
	dog2->run();


	delete dog2;
	dog2 = NULL;
	return(0);
}

第5 行定义了一个Dog 狗定义类时起的类名要尽量贴近这个类让人一看就明白
您这个类是做什么的。
第7 行访问限定符public公有的此外还有private私有的和protected受保护的。
写这个的目的是为了下面我们要调用这些成员不写访问限定符默认是private。关于访问限定符如果是初学者可能会难理解。简单的来说访问限定符就是设置一个成员变量和成员函数的访问权限而已初学者暂时不必要深究什么时候应该用public 和什么时候应该用private。
第8 至11 行定义了一个字符串变量name整形变量age。和一个方法run()。我们在这
个run()里打印相应的狗名和狗的年龄。PSstring 是C++的数据类型方便好用使用频率相当高。
第18 行从栈中实例化一个对象dog1(可以随意起名字)。
第20 至22 行为dog1 的成员变量赋值dog1 的name 赋值叫“旺财”年龄为2 岁。然
后调用run()方法打印dog1 的相关变量的信息。
第24 行从堆中实例化对象使用关键字new 的都是从堆中实例化对象。
第26 行从堆中实例化对象需要开辟内存指针会指向那个内存如果new 没有申请内
存成功p 即指向NULL程序就自动退出下面的就不执行了写这个是为了严谨。
第29 至31 行和dog1 一样为dog2 的成员赋值。
第34 和35 行释放内存将dog2 重新指向NULL。
如果没有语法错误我们完全可以预测到打印的结果。我们学习C 语言的结构体类其实
和结构类似可以说类是结构体的升级版本。
执行下面的指令开始编译。

g++ 03_class_dog_example.cpp -o 03_class_dog_example

编译完成后执行的结果如下。
在这里插入图片描述
通过上面的例子我们已经学习了什么是类和什么是对象。以描述Dog 为一类抽象出来
的从Dog 类中实例出来就是对象实际事物。对象拥有Dog 类里的属性可以从栈中实例化对象亦可从堆中实例化对象。类的编写过程和对象的使用过程大致如上了。我们只需要理解这个步骤明白类的定义和使用即可。

构造函数与析构函数

什么是构造函数构造函数在对象实例化时被系统自动调用仅且调用一次。构造函数出
现在哪里前面我们学过类实际上定义类时如果没有定义构造函数和析构函数编译器就会生成一个构造函数和析构函数只是这个构造和析构函数什么事情也不做所以我们不会注意到一点。
构造函数的特点如下
1构造函数必须与类名同名
2可以重载重载新概念后面学到什么是重载。
3没有返回类型即使是void 也不行。
什么是析构函数与构造函数相反在对象结束其生命周期时系统自动执行析构函数。实
际上定义类时编译器会生成一个析构函数。
析构函数的特点如下
1析构函数的格式为~类名()
2调用时释放内存资源
3~类名()不能加参数
4没有返回值即使是void 也不行。
下面我们通过简单的例子来说明构造函数和析构函数的使用。新建一个目录
04_structor_example编辑一个04_structor_example.cpp 内容如下。

#include <iostream>
#include <string>
using namespace std;

class Dog
{
public:
	Dog();
	~Dog();
};

int main()
{
	Dog dog;
	cout << "构造与析构函数示例" << endl;
	return(0);
}


Dog::Dog()
{
	cout << "构造函数执行" << endl;
}


Dog::~Dog()
{
	cout << "析构函数执行" << endl;
}

我们还是以简单的狗类作为示例定义一个狗类把构造函数和析构函数写上。前面不是
说会自动生成构造函数和析构函数的吗注意是编译时编译器生成的。当我们要使用构造函数和析构函数时需要我们自己在类里添加。
第5 至第10 行定义了一个狗类并在里面写了构造函数和析构函数。
第14 行使用Dog 类实例化一个dog 对象。
第15 行打印一句"构造与析构函数示例"。
第19 至22 行类的函数可以在类里实现也可以在类外实现不过在类外实现时需要使
用“::”此时我们把类的构造函数定义在类的外面打印一句"构造函数执行“。
第14 至27 行类的析造函数定义在类的外面打印一句"析造函数执行”。
执行下面的指令开始编译。

g++ 04_structor_example.cpp -o 04_structor_example

编译完成后执行的结果如下。
在这里插入图片描述
其实执行的结果也是可以预测的在对象实例化时会调用构造函数所以Dog()先执行
然后再在main()函数里继续执行cout<<“构造与析构函数示例”<<endl;。最后对象生命周期结束时才会执行析构函数。

this 指针

一个类中的不同对象在调用自己的成员函数时其实它们调用的是同一段函数代码那么
成员函数如何知道要访问哪个对象的数据成员呢
没错就是通过this 指针。每个对象都拥有一个this 指针this 指针记录对象的内存地址。
在C++中this 指针是指向类自身数据的指针简单的来说就是指向当前类的当前实例对象。
关于类的this 指针有以下特点
1this 只能在成员函数中使用全局函数、静态函数都不能使用this。实际上成员函数
默认第一个参数为T * const this。也就是一个类里面的成员了函数int func(int p)func 的原
型在编译器看来应该是int func(T * const this,int p)。
2this 在成员函数的开始前构造在成员函数的结束后清除。
3this 指针会因编译器不同而有不同的放置位置。可能是栈也可能是寄存器甚至全
局变量。

下面以简单的例子来说明this 的用法。我们还是以狗类为例按上面的this 解释this 只
能够在成员函数使用并可以指向自身数据。我们就可以写这样简单的例子来说明this 的用法。
我们在Qt 里也会遇到this 这个东西下面这个例子就很容易解释Qt 里的this 指针的用法。
新建一个目录05_this_pointer_example编辑一个05_this_pointer_example.cpp 内容如下。

#include <iostream>
#include <string>
using namespace std;

class Dog
{
public:
	string name;
	void func();
};

int main()
{
	Dog dog;
	dog.func();
	return(0);
}


void Dog::func()
{
	this->name = "旺财";
	cout << "小狗的名字叫" << this->name << endl;
}

第21 和22 行在类的成员函数里使用了this 指针并指向了类里的成员name。先将name
赋值叫“旺财”然后我们打印name 的值。
当程序没有语法错误里我们可以预测打印的结果就是“小狗的名字叫旺财”。
执行下面的指令开始编译。

g++ 05_this_pointer_example.cpp -o 05_this_pointer_example

程序执行的结果如下。
在这里插入图片描述

继承

面向对象程序设计中最重要的一个概念是继承。继承允许我们依据另一个类来定义一个类
这使得创建和维护一个应用程序变得更容易。这样做也达到了重用代码功能和提高执行效率的效果。

当创建一个类时您不需要重新编写新的数据成员和成员函数只需指定新建的类继承了
一个已有的类的成员即可。这个已有的类称为基类新建的类称为派生类。在Qt 里大量的使用了这种特性当Qt 里的类不满足自己的要求时我们可以重写这个类就是通过继承需要重写的类来实现自己的类的功能。
一个类可以派生自多个类这意味着它可以从多个基类继承数据和函数。定义一个派生
类我们使用一个类派生列表来指定基类。类派生列表以一个或多个基类命名形式如下

class derived-class: access-specifier base-class

与类的访问修饰限定符一样继承的方式也有几种。其中访问修饰符access-specifier 是public、protected 或private 其中的一个base-class 是之前定义过的某个类的名称。如果未使用访问修饰符access-specifier则默认为private。
下面来捋一捋继承的方式例子都是以公有成员和公有继承来说明其他访问修饰符和其
他继承方式大家可以在教程外自己捋一捋。这个公有成员和继承方式也没有什么特别的无非就是不同的访问权限而已可以这样简单的理解。

  1. 公有继承public当一个类派生继承公有基类时基类的公有成员也是派生类的公有成员基类的保护成员也是派生类的保护成员基类的私有成员不能直接被派生类访问但是可以通过调用基类的公有和保护成员来访问。
  2. 保护继承protected当一个类派生继承保护基类时基类的公有和保护成员将成为派生类的保护成员。
  3. 私有继承private当一个类派生继承私有基类时基类的公有和保护成员将成为派生类的私有成员。
    下面我们还是以狗类为例在2.2.1 小节里我们定义的狗类已经定义了nameage 和run()方法。假设我们不想重写这个狗类而是新建一个Animal 类让狗类去继承这个Animal 类。
    假设是公有继承那么我们是不是可以在狗类实例的对象里去使用继承Animal 类里的成员呢
    带着这个疑问我们使用下面的例子来说明。
    新建一个目录06_inherit_example编辑一个06_inherit_example.cpp 内容如下。
#include <iostream>
#include <string>
using namespace std;


/*动物类抽象出下面两种属性
 * *颜色和体重是每种动物都具有的属性
 */
class Animal
{
public:
	/* 颜色成员变量*/
	string color;
	/* 体重成员变量*/
	int weight;
};


/*让狗类继承这个动物类并在狗类里写自己的属性。
 * *狗类拥有自己的属性nameagerun()方法同时也继承了
 * *动物类的color和weight的属性
 */
class Dog : public Animal
{
public:
	string	name;
	int	age;
	void run();
};

int main()
{
	Dog dog;
	dog.name	= "旺财";
	dog.age		= 2;
	dog.color	= "黑色";
	dog.weight	= 120;
	cout << "狗的名字叫" << dog.name << endl;
	cout << "狗的年龄是" << dog.age << endl;
	cout << "狗的毛发颜色是" << dog.color << endl;
	cout << "狗的体重是" << dog.weight << endl;
	return(0);
}

第21 行Animal 作为基类Dog 作为派生类。Dog 继承了Animal 类。访问修饰符为public
公有继承。
执行下面的指令开始编译。

g++ 06_inherit_example.cpp -o 06_inherit_example

编译完成执行的结果为如下。
在这里插入图片描述

重载

C++ 允许在同一作用域中的某个函数和运算符指定多个定义分别称为函数重载和运算符
重载。

重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明但
是它们的参数列表和定义实现不相同。
当您调用一个重载函数或重载运算符时编译器通过把您所使用的参数类型与定义中的参
数类型进行比较决定选用最合适的定义。选择最合适的重载函数或重载运算符的过程称为重载决策。

函数重载

在同一个作用域内可以声明几个功能类似的同名函数但是这些同名函数的形式参数指
参数的个数、类型或者顺序必须不同。我们不能仅通过返回类型的不同来重载函数。在Qt
源码里运用了大量的函数重载所以我们是有必要学习一下什么是函数重载。不仅在C++在其他语言的里都能看见函数重载。因为需要不同所以有重载各种各样的函数。
下面通过一个小实例来简单说明一下函数重载的用法。我们还是以狗类为说明现在假设
有个需求。我们需要打印狗的体重分别以整数记录旺财的体重和小数记录旺财的体重同时以整数打印和小数打印旺财的体重。那么我们可以通过函数重载的方法实现这个简单的功能。

新建一个目录07_func_overloading编辑一个07_func_overloading.cpp 内容如下。

#include <iostream>
#include <string>
using namespace std;

class Dog
{
public:
	string name;
	void getWeight( int weight )
	{
		cout << name << "的体重是" << weight << "kG" << endl;
	}


	void getWeight( double weight )
	{
		cout << name << "的体重是" << weight << "kG" << endl;
	}
};

int main()
{
	Dog dog;
	dog.name = "旺财";
	dog.getWeight( 10 );
	dog.getWeight( 10.5 );
	return(0);
}

第9 行写了一个方法getWeight(int weight)以int 类型作为参数。
第13 行以相同的函数名getWeight不同的参数类型double weight这样就构成了函数
重载。
第22 行与第23 行分别传进参数不同的参数程序就会匹配不同的重载函数。
执行下面的指令编译。

g++ 07_func_overloading.cpp -o 07_func_overloading

程序执后的结果如下。
在这里插入图片描述

通过上面的例子我们可以知道重载函数的使用方法避免用户传入的参数类型有可能用
户传入的参数类型不在我们写的重载函数里假若用户传入了一个字符串类型这样编译器就会匹配不到相应的重载函数编译时就会报错。其实我们还可以多写几个重载函数设置多几种类型如string 类型char 类型float 类型等。

运算符重载

运算符重载的实质就是函数重载或函数多态。运算符重载是一种形式的C++多态。目的在
于让人能够用同名的函数来完成不同的基本操作。要重载运算符需要使用被称为运算符函数的特殊函数形式运算符函数形式operatorpargument-listoperator 后面的’p’为要重载的运算符符号。重载运算符的格式如下

<返回类型说明符> operator <运算符符号>(<参数表>)
{
	<函数体>
}

下面是可重载的运算符列表
在这里插入图片描述
在这里插入图片描述
下面是不可重载的运算符列表
在这里插入图片描述

根据上表我们知道可以重载的运算符有很多我们以重载“+”运算符为例实际上用重载
运算符我们在实际应用上用的比较少我们只需要了解和学习这种思想即可。
下面的实例使用成员函数演示了运算符重载的概念。在这里对象作为参数进行传递对象
的属性使用this 运算符进行访问。下面还是以我们熟悉的狗类为例。声明加法运算符用于把两个Dog 对象相加的体重相加返回最终的Dog 对象然后得到第三个Dog 对象的体重。
新建一个目录08_operator_example编辑一个08_operator_example.cpp 内容如下。

#include <iostream>
#include <string>
using namespace std;

class Dog
{
public:
	int weight;
	Dog operator+( const Dog &d )
	{
		Dog dog;
		dog.weight = this->weight + d.weight;
		return(dog);
	}
};

int main()
{
	Dog	dog1;
	Dog	dog2;
	Dog	dog3;
	dog1.weight	= 10;
	dog2.weight	= 20;
	dog3		= dog1 + dog2;
	cout << "第三只狗的体重是" << dog3.weight << endl;
	return(0);
}

第9 至13 行重载“+”运算符注意函数必须与类名同名把Dog 对象作为传递使用
this 运算符进行访问。然后返回一个dog 对象。
执行下面指令进行编译。

g++ 08_operator_example.cpp -o 08_operator_example

编译完成后运行的结果如下。

在这里插入图片描述

结果可以预知的重载运算符“+”可以把两个对象进行相加。在普通的算术运算符“+”
是不能将两个对象进行相加的所以我们重载运算符的意义可以体现在这里。

多态

C++多态意味着调用成员函数时会根据调用函数的对象的类型来执行不同的函数
形成多态必须具备三个条件

  1. 必须存在继承关系
  2. 继承关系必须有同名虚函数其中虚函数是在基类中使用关键字virtual 声明的函数在派
    生类中重新定义基类中定义的虚函数时会告诉编译器不要静态链接到该函数
  3. 存在基类类型的指针或者引用通过该指针或引用调用虚函数。
    这里我们还需要理解两个概念

虚函数
是在基类中使用关键字virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时
会告诉编译器不要静态链接到该函数。我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数这种操作被称为动态链接或后期绑定。虚函数声明如下virtual
ReturnType FunctionName(Parameter) 虚函数必须实现如果不实现编译器将报错

纯虚函数
若在基类中定义虚函数以便在派生类中重新定义该函数更好地适用于对象但是您在基
类中又不能对虚函数给出有意义的实现这个时候就会用到纯虚函数。纯虚函数声明如下
virtual void funtion1()=0; 纯虚函数一定没有定义纯虚函数用来规范派生类的行为即接口。
包含纯虚函数的类是抽象类抽象类不能定义实例但可以声明指向实现该抽象类的具体类的指针或引用。
上面那些概念大家可以捋一捋毕竟C++概念还是挺多的。为什么说到多态要与虚函数和
纯虚函数扯上关系光说概念没有实例确实难理解。下面我们还是以我们熟悉的狗类和动物类另外加一个猫类进行多态的讲解。
新建一个目录09_polymorphism_example编辑一个09_polymorphism_example.cpp 内容如下。PS: polymorphism 翻译多态的意思笔者就以这种方式命名例程了。

#include <iostream>
#include <string>
using namespace std;

/* 定义一个动物类*/
class Animal
{
public:
	virtual void run()
	{
		cout << "Animal的run()方法" << endl;
	}
};

/* 定义一个狗类并继承动物类*/
class Dog : public Animal
{
public:
	void run()
	{
		cout << "Dog的run()方法" << endl;
	}
};

/* 定义一个猫类并继承动物类*/
class Cat : public Animal
{
public:
	void run()
	{
		cout << "Cat的run()方法" << endl;
	}
};

int main()
{
	/* 声明一个Animal的指针对象注并没有实例化*/
	Animal *animal;
	/* 实例化dog对象*/
	Dog dog;
	/* 实例化cat对象*/
	Cat cat;

	/* 存储dog对象的地址*/
	animal = &dog;
	/* 调用run()方法*/
	animal->run();

	/* 存储cat对象的地址*/
	animal = &cat;
	/* 调用run()方法*/
	animal->run();
	return(0);
}

第9 行、第18 行和第28 行都有一个run()方法。其中我们可以看到基类Animal 类的run()
方法前面加了关键字virtual。这样让基类Animal 类的run()方法变成了虚函数。在这个例子里我们可以知道虚函数是C++中用于实现多态(polymorphism)的机制。核心理念就是通过基类访问派生类定义的函数。简单的来说上面的实例是基类Animal 声明了一个指针animal。然后通过基类的指针来访问Dog 类对象与Cat 类的对象的run()方法前提是基类的run()方法必须声明为虚函数如果不声明为虚函数基类的指针将访问到基类自己的run()方法。我们可以尝试把virtual 关键字去掉再重新编译测试如果不加关键字virtual 会是什么情况。
第44 行和第49 行可以理解是animal 指针实例化的过程。当基类的run()方法定义成虚函
数编译器不静态链接到该函数它将链接到派生类的run()方法进行实例化。
执行下面的指令编译。

g++ 09_polymorphism_example.cpp -o 09_polymorphism_example

编译完成执行的结果如下。
在这里插入图片描述

数据封装

封装是面向对象编程中的把数据和操作数据的函数绑定在一起的一个概念这样能避免受
到外界的干扰和误用从而确保了安全。数据封装引申出了另一个重要的OOP 概念即数据隐藏。
数据封装是一种把数据和操作数据的函数捆绑在一起的机制数据抽象是一种仅向用户暴
露接口而把具体的实现细节隐藏起来的机制C++ 通过创建类来支持封装和数据隐藏public、protected、private。
其实我们在第2.2 小节开始就已经接触了数据封装。在C++程序中任何带有公有和私有
成员的类都可以作为数据封装和数据抽象的实例。通常情况下我们都会设置类成员状态为私有private除非我们真的需要将其暴露这样才能保证良好的封装性。这通常应用于数据成员但它同样适用于所有成员包括虚函数。
下面我们还是以狗类为例增加一个食物的方法addFood(int number)。将获得食物的方法
设定在public 下这样addFood(int number)方法就暴露出来了也就是对外的接口。然后我们设置狗类的私有成员(private)食物的份数total。我们在这个教程里第一次使用private在这章节里我们也可以学到什么时候该使用private 什么时候使用public。total 为获得的食物总数然后我们还写一个公开的方法getFood()在public 下通过getFood()来打印出小狗总共获得了几份食物。
新建一个目录10_encapsulation_example编辑一个10_encapsulation_example.cpp 内容如下。
PS: encapsulation 翻译封装的意思笔者就以这种方式命名例程了。

#include <iostream>
#include <string>
using namespace std;

class Dog
{
public:
	string name;

	Dog( int i = 0 )
	{
		total = i;
	}


	void addFood( int number )
	{
		total = total + number;
	}


	int getFood()
	{
		return(total);
	}


private:
	int total;
};


int main()
{
	Dog dog;

	dog.name = "旺财";

	dog.addFood( 3 );
	dog.addFood( 2 );

	cout << dog.name << "总共获得了" << dog.getFood() << "份食物" << endl;

	return(0);
}

第10 至第13 行在构造函数里初始化total 的数量不初始化total 的数量默认是随int 类
型的数。所以我们需要在构造函数里初始化也体现了构造函数的功能一般是在构造函数里初始化。不要在类内直接赋值初始化有可能有些编译器不支持。
第15 至17 行addFood(int number)在这个方法里将获得的食物份数赋值给total。
第19 至21getFood()在这个方法里将返回食物的总份数。通过调用这个方法即可
访问私有成员的total 总数。
第33 和34 行添加食物的份数。
第36 行打印食物的总份数。
执行下面的指令编译。

g++ 10_encapsulation_example.cpp -o 10_encapsulation_example

编译完成执行的结果如下。
在这里插入图片描述

数据抽象

数据抽象是指只向外界提供关键信息并隐藏其后台的实现细节即只表现必要的信息
而不呈现细节。数据抽象是一种依赖于接口和实现分离的编程设计技术。
数据抽象的好处

  1. 类的内部受到保护不会因无意的用户级错误导致对象状态受损。
  2. 类实现可能随着时间的推移而发生变化以便应对不断变化的需求或者应对那些要
    求不改变用户级代码的错误报告。
    举个简单的例子比如我们生活中的手机。手机可以拍照、听音乐、收音等等。这些都是手机上的功能用户可以直接使用。但是拍照的功能是如何实现的是怎么通过摄像头取像然后怎么在屏幕上显示的过程作为用户是不需要知道的。也就是暴露的不用太彻底用户也不必须知道这种功能是如何实现的只需要知道如何拍照即可。
    就C++ 编程而言C++ 类为数据抽象提供了可能。它们向外界提供了大量用于操作对象数据的公共方法也就是说外界实际上并不清楚类的内部实现。
    其实像cout 这个对象就是一个公共的接口我们不必要知道cout 是如何在屏幕上显示内容的。cout 已经在底层实现好了。
    在上一节我们已经学习过数据封装数据封装是一种把数据和操作数据的函数捆绑在一起的机制而数据抽象是一种仅向用户暴露接口而把具体的实现细节隐藏起来的机制。

C++ 程序中任何带有公有和私有成员的类都可以作为数据抽象的实例。例子略例子可
参考上2.2.5 小节的例子。

接口抽象类

接口描述了类的行为和功能而不需要完成类的特定实现。C++ 接口是使用抽象类来实现
的抽象类与数据抽象互不混淆数据抽象是一个把实现细节与相关的数据分离开的概念。如果类中至少有一个函数被声明为纯虚函数则这个类就是抽象类。纯虚函数是通过在声明中使用"= 0" 来指定的。
设计抽象类通常称为ABC的目的是为了给其他类提供一个可以继承的适当的基类。
抽象类不能被用于实例化对象它只能作为接口使用。如果试图实例化一个抽象类的对象会导致编译错误。
因此如果一个ABC 的子类需要被实例化则必须实现每个虚函数这也意味着C++ 支
持使用ABC 声明接口。如果没有在派生类中重写纯虚函数就尝试实例化该类的对象会导致编译错误。可用于实例化对象的类被称为具体类。
根据概念我们来写个实例来说明抽象类。
还是以狗类为说明例程与2.2.4 小节类似只是Aninmal 类的run()方法定义为纯虚函数
纯虚函数不用实现由派生类Dog 和Cat 类实现重写即可。
新建一个目录11_abstract_class编辑一个11_abstract_class.cpp 内容如下。

#include <iostream>

using namespace std;

/* 定义一个动物类*/
class Animal
{
public:
	virtual void run() = 0;
};

/* 定义一个狗类并继承动物类*/
class Dog : public Animal
{
public:
	void run()
	{
		cout << "Dog的run()方法" << endl;
	}
};

/* 定义一个猫类并继承动物类*/
class Cat : public Animal
{
public:
	void run()
	{
		cout << "Cat的run()方法" << endl;
	}
};

int main()
{
	/* 实例化dog对象*/
	Dog dog;

	/* 实例化cat对象*/
	Cat cat;

	/* dog调用run()方法*/
	dog.run();

	/* cat调用run()方法*/
	cat.run();

	return(0);
}

执行下面指令进行程序编译。

g++ 11_abstract_class.cpp -o 11_abstract_class

程序运行的结果如下。
在这里插入图片描述

虽然结果和例程与2.2.4 小节一样但是却表现了两种不同的思想。学C++重要的是思想
当我们对这种思想有一种的了解后不管是Qt 或者其他C++程序我们都能快速学习和了解。
C++的内容就到此结束了。在这个C++基础中我们的例子非常简单也十分之易懂重要的是理解概念许多C++的课程都是以C++的功能甚至是很复杂的算法作讲解内容复杂且多。
只要我们理解好上面的C++的基础对学习C++有很大的帮助不要求对C++有很深的理解至少在我们后面学习Qt 时已经大概了解Qt 中的C++语法。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: c++