0%

C++学习笔记

前言

小鲸鱼有C语言的编程基础,所以C++与C相似的部分就简单带过啦~

欢迎进入Cpp的世界

1
2
3
4
5
6
7
8
9
10
11
12
/* Code language: C++ */
#include <iostream> //cout,cin

int main() {
std::cout << "Hello!" << std::endl;
std::cout << "Welcome to c++!" << std::endl;
return 0;
}
/* Output
* Hello!
* Welcome to c++!
*/

目录


基础知识

基本数据类型: 整数、实数、字符、布尔数据
基本运算: 算术运算、关系运算、逻辑运算、位运算、逗号运算、条件运算

字符集: A~Z, a~z, 0~9, ! # % ^ & * _ + = - ~ < > / \ ' " ; . , : ? ( ) [ ] { } |
词法记号: 关键字、标识符、文字、分隔符、运算符、空白符(包括注释)

标识符构成规则:

  • 以大写字母、小写字母或下划线_开始
  • 可以由以大写字母、小写字母、下划线_或数字0~9组成
  • 区分大小写
  • 不能是C++关键字或操作符

基础数据类型

四种基础数据类型

整数类型: int, 符号(signed, unsigned), 范围(short, long, long long)
字符类型: char, 本质也是整型
浮点数: float(单精度), double(双精度), long double(扩展精度)
布尔类型: bool -> 值为true or false -> 非0整数转布尔都是true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* Code language: C++ */
int main() {
/* 整型 */
cout << "sizeof(int)=" << sizeof(int) << ", sizeof(short)=" << sizeof(short) << ", sizeof(long)=" << sizeof(long) << endl;
/* 浮点型 */
cout << "sizeof(float)=" << sizeof(float) << ", sizeof(double)=" << sizeof(double) << ", sizeof(long double)=" << sizeof(long double) << endl;
/* 字符型、布尔型 */
cout << "sizeof(char)=" << sizeof(char) << ", sizeof(bool)=" << sizeof(bool) << endl;
return 0;
}
/* Output
* sizeof(int)=4, sizeof(short)=2, sizeof(long)=4
* sizeof(float)=4, sizeof(double)=8, sizeof(long double)=8
* sizeof(char)=1, sizeof(bool)=1
*/

常量

常量是在程序运行的整个过程中其值始终不可改变的量

整型:

  • 十进制: 除数字0以外不能以0开头
  • 二进制: 前缀0b, 数字范围0, 1
  • 八进制: 前缀0, 数字范围0-7
  • 十六进制: 前缀0x, 数字范围0-9-ABCDEF(不区分大小写)
  • 后缀L/l的为long, 后缀U/u的为unsigned
1
2
3
4
5
6
7
8
9
10
/* Code language: C++ */
int main() {
int x, y, z, u;
x = 3, y = 027, z = 0xFF, u = 0b11011010;
cout << "x, y, z, u = " << x << ", " << y << ", " << z << ", " << u << endl;
return 0;
}
/* Output(整型默认输出格式为十进制)
* x, y, z, u = 3, 23, 255, 218
*/

浮点数:

  • 一般形式: 12.0, 1.5
  • 指数形式: 3.14159e10, -1E3, e9, 1.758962E
  • 浮点型常量默认是double

字符常量: 单引号内的一个字符'a'
转义字符:

转义字符 ASCII码(十六进制) 含义
\(\\a\) 07 响铃
\(\\n\) 0A 换行
\(\\t\) 09 水平制表符
\(\\v\) 0B 垂直制表符
\(\\b\) 08 退格
\(\\r\) 0D 回车
\(\\f\) 0C 换页
'\ \' 5C 斜杠
\(\\"\) 22 双引号
\(\\'\) 27 单引号
\(\\?\) 3F 问号

符号常量

1
2
3
4
5
6
7
8
9
10
11
12
/* Code language: C++ */
int main() {
const double PI = 3.1415926;
int const N(314159);
cout << "PI = " << PI << endl;
cout << "N = " << N << endl;
return 0;
}
/* Output
* PI = 3.14159
* N = 314159
*/

定义:

  • const 数据类型说明符 常量名 = 常量值;
  • 数据类型说明符 const 常量名 = 常量值;
  • 符号常量在定义时一定要初始化,在程序中间不能改变其值

constexpr

严格意义上来说const的含义是"只读", 而constexpr的含义才是"常量", 即编译期间即可求得值的

运算

算数运算: + - * / % ++ -- += -= *= /= %= <<= >>= ^= |=
赋值运算: =
逗号运算
关系运算: > < >= <= == !=, 结果为bool
逻辑运算: &&(与) ||(或) !(非), 结果为bool
条件运算: 表达式1 ? 表达式2 : 表达式3

按位与(&): 将某位置置零或取指定位
按位或(|): 将某位置置1
按位异或(^): 将某位置翻转(与1异或)
按位取反(~)

左移(<<): 低位补0, 高位舍弃
右移(>>): 低位舍弃,高位补符号位

输入输出流

操纵符名 含义
dec 数值数据采用十进制表示
hex 数值数据采用十六进制表示
oct 数值数据采用八进制表示
ws 提取空白符
endl 插入换行符,并刷新流
ends 插入空字符
setsprecision(int) 设置浮点数的小数位数(包括小数点)
setw(int) 设置域宽

逻辑语句

条件语句

if语句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* Code language: C++ */
int main() {
int x, y;
cin >> x;
cin >> y;
if (x > y)
cout << "x > y" << endl;
else if (x < y)
cout << "x < y" << endl;
else {
cout << "x == y" << endl;
return x;
}
return 0;
}
/* Output
* 20210424
* 19000101
* x > y
*/
switch语句

很简单,看例子吧~switch是可以用if-else if-else语句代替的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* Code language: C++ */
int main() {
int day;
cin >> day;
switch (day)
{
case 0: cout << "Sunday" << endl; break;
case 1: cout << "Monday" << endl; break;
case 2: cout << "Tuesday" << endl; break;
case 3: cout << "Wednesday" << endl; break;
case 4: cout << "Thursday" << endl; break;
case 5: cout << "Friday" << endl; break;
case 6: cout << "Saturday" << endl; break;
default:
cout << "Day out of range Sunday .. Saturday" << endl; break;
}
return 0;
}
/* Output
* 6
* Saturday
*/

循环语句

while语句

while语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Code language: C++ */
int main() {
int sum = 1;
cout << "sum = ";
while (sum < 314) {
sum += sum;
cout << sum << ends;
}
cout << endl;
return 0;
}
/* Output
* sum = 2 4 8 16 32 64 128 256 512
*/

do while语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Code language: C++ */
int main() {
int sum = 1023;
cout << "sum = ";
do {
sum &= sum - 1;
cout << sum << ends;
} while (sum > 0);
cout << endl;
return 0;
}
/* Output
* sum = 1022 1020 1016 1008 992 960 896 768 512 0
*/
for语句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* Code language: C++ */
#include <cstdlib>
//#include <stdlib.h>
int main() {
srand((unsigned int)time(NULL));
int nums[] = { rand(), rand(), rand() , rand() , rand() , rand() };
cout << "nums = ";
for (int i = 0; i < 6; i++)
cout << nums[i] << ends;
cout << endl;
return 0;
}
/* Output
* nums = 16298 3771 15596 32089 17283 24929
*/

注:

  • void srand(unsigned int seed); //设置随机数种子
  • 建议随机数种子设为(unsign int)time(NULL) <- 需要#include <time.h>
  • int rand(void)返回一个伪随机数,可通过取余控制范围
1
2
3
4
5
6
7
8
9
10
11
12
/* Code language: C++ */
int main() {
char str[] = "I-like-C++";
cout << "string = ";
for (auto s : str)
cout << s << ends;
cout << endl;
return 0;
}
/* Output
* string = I - l i k e - C + +
*/
控制语句

指break, continue, goto

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* Code language: C++ */
int main() {
int nums[] = { 1, 512, 1024 };
for (int i = -3; ;) {
if (i < 0)goto add;
cout << "nums[" << i << "] = " << nums[i++] << endl;
add:
if (i < 0) {
i++;
cout << "i = " << i << endl;
}
else if (i >= 3)break;
else continue;
}
return 0;
}
/* Output
* i = -2
* i = -1
* i = 0
* nums[1] = 1
* nums[2] = 512
* nums[3] = 1024
*/

自定义类型

typedef

1
2
3
/* Code language: C++ */
typedef int ElementType;
using Number = ElementType;

枚举类型

不限定作用域的枚举类型

底层实际上是整型,默认列表中的值为0,1,2……
但允许自定义值,后续值顺延

1
2
3
4
5
6
7
8
9
10
/* Code language: C++ */
enum Weekday { SUN = 7, MON = 1, TUE, WED, THU, FRI, SAT };
int main() {
Weekday x = SUN, y = FRI;
cout << "x, y = " << x << ends << y << endl;
return 0;
}
/* Output
* x, y = 7 5
*/
限定作用域的枚举类型

定义: enum class 枚举类型名 :底层类型名 {枚举值列表};

1
2
3
4
5
6
7
8
9
10
/* Code language: C++ */
int main() {
enum class HairColor :char {Black = 'b', White = 'w', Gray = 'g', Brown = 'B'};
HairColor myhair = HairColor::Black;
cout << (char)myhair << endl;
return 0;
}
/* Output
* b
*/

自动类型

auto: 根据初值自动推断类型(编译器完成类型判断) decltype: 根据变量决定类型

1
2
3
4
5
6
7
8
/* Code language: C++ */
int main() {
// 根据初值推断变量类型 (double)
auto i = 1.0;
// j与变量i类型相同但与i的初值无关 (double)
decltype(i)j;
return 0;
}

函数

函数: 定义好的、可重用的功能模块

函数定义

1
2
3
4
5
/* Code language: C++ */
类型标识符 函数名(形式参数表)
{
函数体
}
  • 在函数定义前调用函数需要先声明函数原型
  • 函数原型: 类型标识符 函数名(形式参数表)
  • 函数调用: 函数名(实际参数表)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/* Code language: C++ */
bool isPrime(int n);

int main() {
int x;
cout << "请输入一个正整数:";
cin >> x;
if (isPrime(x))
cout << x << "是质数" << endl;
else
cout << x << "不是质数" << endl;
return 0;
}

bool isPrime(int n) {
for (int i = 2; i <= sqrt(n); i++)
if (n % i == 0)return false;
return true;
}
/* Output
* 请输入一个正整数:29
* 29是质数
* 请输入一个正整数:27
* 27不是质数
*/

函数的嵌套、递归调用不再赘述。

参数传递

  • 在函数被调用时才分配形参的存储单元
  • 实参可以是常量、变量或表达式
  • 实参类型必须与形参相符
  • 值传递是传递参数值,即单向传递
  • 引用(&)传递可以实现双向传递
  • 常引用(如const int &num)作参数可以保障实参数据的安全
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* Code language: C++ */
void swap(int &x, int &y) {
int tmp;
tmp = x;
x = y;
y = tmp;
}

int main() {
int x = 2, y = 5;
swap(x, y);
cout << x << ends << y << endl;
return 0;
}
/* Output
* 5 2
*/

带默认值的参数

  • 可以预先设置默认的参数值,调用时如给出实参,则采用实参值,否则采用预先设置的默认值
  • 带默认参数的形参必须在不带默认参数的形参右方,即带默认参数的形参右边不能有不带默认参数的形参
  • 调用函数是实参与形参结合顺序是由左向右,不能由形参名指定
  • 如果有函数声明,则默认参数应该在函数声明中指定而函数定义中无需指定(两个地方都指定默认参数会报错"重定义默认参数", 在定义中指定而声明中不指定则定义前声明后的函数调用无法使用默认参数)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* Code language: C++ */
int prt(int x = 9, int y = 18);

int main() {
prt();
return 0;
}

int prt(int x, int y) {
cout << x << ends << y << endl;
return 0;
}
/* Output
* 9 18
*/

函数重载

C++允许函数直接同名,编译器根据形参和实参个数和类型的最佳匹配决定调用函数(不能根据返回值区分)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* Code language: C++ */
int prt(int x, int y = 18);
void prt(double x, double y = 3.14159);

int main() {
prt(1);
prt(1.0);
return 0;
}

int prt(int x, int y) {
cout << "调用第一个函数: " << x << ends << y << endl;
return 0;
}

void prt(double x, double y) {
cout << "调用第二个函数: " << x << ends << y << endl;
}
/* Output
* 调用第一个函数: 1 18
* 调用第二个函数: 1 3.14159
*/

内联函数

  • 声明时使用关键字inline
  • 编译时进行函数体替换,节省了参数传递、控制转移等的开销
  • 内联函数不能有循环语句和switch语句
  • 对内联函数不能进行异常接口声明
  • 编译器很智能,简单函数会自动当成内联函数

类与对象

类是同一类对象的抽象,对象是类的某一特定实体

类与对象的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
/* 类的定义 */
class 类名称
{
public:
公有成员(外部接口)
private:
私有成员
protected:
保护型成员
};

/* 对象的定义 */
类名称 对象名;

注意类定义的{}后面有分号;

类成员的访问控制

  • 公有类型成员: public声明,是类与外部接口,任何外部函数都能访问
  • 私有类型成员: private声明,只允许本类成员函数访问,外部函数无法访问
  • 保护类型成员: protected声明,与private类似,差别在于继承和派生时表现不同
  • 类内成员不声明访问权限时默认是private
  • 类内成员相互访问直接使用成员名
  • 类外使用 对象名.成员名对象指针->成员名 的方式访问public成员

类内初始值

  • 可以为数据成员提供一个类内初始值,在创建对象时类内初始值用于初始化成员对象
  • 没有初始值的类内成员默认初始化

类的成员函数

  • 类中声明函数,类外定义函数(需加类名限定)
  • 也可以在类中定义函数
  • 允许使用重载函数和带默认值的函数

类与对象定义示例

程序中定义了People类,数据成员有名字name(string), 性别sex(枚举类,♀,♂,⚧), 年龄(int)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/* Code language: C++ */
enum gender { Female, Male, Other };

class People
{
public:
void ShowMyself();
void SetName(string NewName) {
name = NewName;
}
private:
string name = "Wu Ming";
gender sex = Female;
int age = 18;
};

void People::ShowMyself() {
cout << "My name is " << name << endl;
cout << "I'm a " << (sex == Female ? "Girl" : "Man") << endl;
cout << "I'm " << age << " years old" << endl;
}

int main() {
People myself;
myself.SetName("小鲸鱼");
myself.ShowMyself();
return 0;
}
/* Output
* My name is 小鲸鱼
* I'm a Girl
* I'm 18 years old
*/

构造函数

  • 构造函数: 在创建对象时将对象初始化到特定状态
  • 构造函数形式: 构造函数名与类名相同,无返回类型,可重载,可带默认参数
  • 默认构造函数: 无形参或形参全部有默认值的构造函数
  • 隐含构造函数: 编译器自动为没有定义构造函数的类生成的构造函数
    • 没有形参,不为数据成员设置初始值
    • 类内成员用定义的初始值初始化,没有初始值的类内成员以默认方式初始化
    • =default在定义了构造函数的情况下使用默认构造函数
  • 委托构造函数: 使用其他构造函数初始化对象
  • 复制构造函数: 用已经存在的对象初始化同类型新对象
    • 复制构造函数的形参为本类的对象引用类名(const 类名 &对象名);
    • 隐含复制构造函数: 编译器在未定义类的构造函数时默认生成的复制构造函数, 作用是将已存在的对象数据成员值初始化新对象对应数据成员
    • =delete禁用隐含的默认复制构造函数
    • 复制构造函数被调用的情况
      • 使用已存在的对象初始化新对象
      • 函数形参是类的对象,使用实参对象初始化形参对象
      • 函数返回值是类的对象,使用return语句中的对象初始化无名对象返回主调函数(也可以使用移动构造函数)
    • 对象的浅层复制: 对象成员一一对应复制
    • 对象的深层复制: 对象成员是指针时,不应该复制指针本身,而是复制指针所指对象
  • 移动构造(C++ 11) - 移动构造函数
    • 源对象的资源直接转移给目标对象
    • 发生在临时对象身上

构造函数示例

在上一个示例的基础上添加了三种构造函数(移动构造函数会在后面添加)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/* Code language: C++ */
enum gender { Female, Male, Other };

class People
{
public:
People() = default;/* 保留隐含的默认构造函数 */
People(string myname, gender mysex, int myage);/* 构造函数的声明 */
/* 委托构造函数 */
People(string myname) :People(myname, Female, 18) {
cout << "调用了委托构造函数" << endl;
}
People(const People &p); /* 复制构造函数 */
//People(const People &p) = delete; /* 复制构造函数 */

/* 类的成员函数 */
void ShowMyself();
void SetName(string NewName) {
name = NewName;
}
private:
/* 类的数据成员 */
string name = "Wu Ming";
gender sex = Female;
int age = 18;
};

/* People类的构造函数定义(使用列表初始化成员) */
People::People(string myname, gender mysex, int myage) :name(myname), sex(mysex), age(myage) {
cout << "New People: " << myname << ends << (mysex == Female ? "Girl" : "Man") << ends << myage << endl;
}

/* 复制构造函数 */
People::People(const People &p) :name(p.name), sex(p.sex), age(p.age) {
cout << "Copy " << name << " !" << endl;
}

void People::ShowMyself() {
cout << "My name is " << name << endl;
cout << "I'm a " << (sex == Female ? "Girl" : "Man") << endl;
cout << "I'm " << age << " years old" << endl;
}

int main() {
People myself("小鲸鱼", Female, 16);
People myfriend("小金鱼");
People Whale = myself;
return 0;
}
/* Output
* New People: 小鲸鱼 Girl 16
* New People: 小金鱼 Girl 18
* 调用了委托构造函数
* Copy 小鲸鱼 !
*/

析构函数

  • 作用: 对象被删除前做些清理工作
  • 析构函数形式: ~类名(); 无返回类型,无形参
  • 编译器自动生成一个空析构函数,如果未声明析构函数
  • 对象生存期结束前调用
1
2
3
4
5
/* Code language: C++ */
/* 析构函数示例 */
~People() {
cout << name << " has been delete!" <<endl;
}

类的组合

  • 类的对象成员是其他类的对象
  • 初始化组合类不仅要初始化基本数据成员,还要初始化对象成员

前向引用声明

  • 类似函数的声明,在定义类前声明类class 类名;
  • 即使声明了类但在没有定义类前不能声明该类的对象,只能使用该类的符号(例如指针)

静态成员

  • 使用关键字static声明,该类所有对象共享
  • (非常量/const的静态成员)必须在类外定义和初始化

类的友元

  • 友元是一种破坏数据封装和数据隐藏的机制
  • 通过将一个模块声明为另一个模块的友元,一个模块可以引用另一个模块本是隐藏的信息
  • 友元函数
    • 在类中由关键字friend修饰声明的非成员函数
    • 函数体中可以通过对象名访问private和protected成员
  • 友元类
    • 类中由关键字friend修饰声明的非成员类
    • 友元类中可以通过对象名访问private和protected成员
  • 友元关系是单向的(A是B的友元\(\neq\)B是A的友元)

类的组合综合示例

  • 为People增加了析构函数(虽然在这里没什么必要,仅做演示)
  • 为People添加了统计People对象数量的静态成员并在类外进行了初始化和定义
  • 在上一个示例的基础上定义了组合类Family, 组合类中有4个People类,并且组合类被声明为People的友元类,使得Family中允许直接访问People类中的private成员
  • 定义了类外的友元函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/* Code language: C++ */
/* 枚举类:性别(女、男、非二元性别) */
enum gender { Female, Male, Other };

class People
{
friend class Family;/* 声明Family为People的友元类 */
public:
People() = default;/* 保留隐含的默认构造函数 */
People(string myname, gender mysex, int myage);/* 构造函数的声明 */
/* 委托构造函数 */
People(string myname) :People(myname, Female, 18) {
cout << "调用了委托构造函数" << endl;
}
People(const People &p); /* 复制构造函数 */
//People(const People &p) = delete; /* 禁用复制构造函数 */

/* 析构函数 */
~People() {
cout << name << " has been delete!" <<endl;
--count;
}

/* 类的成员函数 */
void ShowMyself();
void SetName(string NewName) {
name = NewName;
}
string GetName() {
return this->name;
}

/* 类的静态成员函数 */
static void Showcount() {
cout << "There are " << count << " People!" << endl;
}

/* 友元函数 */
friend void IsFriend(const People &p, const People &q);
private:
/* 类的数据成员 */
string name = "Wu Ming";
gender sex = Female;
int age = 18;
/* 类的静态数据成员 */
static int count;
};

/* 初始化类的静态数据成员 */
int People::count = 0;

/* People类的构造函数定义(使用列表初始化成员) */
People::People(string myname, gender mysex, int myage) :name(myname), sex(mysex), age(myage) {
cout << "New People: " << myname << ends << (mysex == Female ? "Girl" : "Man") << ends << myage << endl;
++count;
}

/* 复制构造函数 */
People::People(const People &p) :name(p.name), sex(p.sex), age(p.age) {
cout << "Copy " << name << " !" << endl;
++count;
}

void People::ShowMyself() {
cout << "My name is " << name << endl;
cout << "I'm a " << (sex == Female ? "Girl" : "Man") << endl;
cout << "I'm " << age << " years old" << endl;
}

/* People的友元函数,可以访问People中的private和protected成员 */
void IsFriend(const People &p, const People &q) {
if (p.age == q.age && p.sex == q.sex)
cout << p.name << " and " << q.name << " are good friends!" << endl;
}

/* 组合类Family */
class Family
{
public:
/* 构造函数 */
Family(People &mother, People &father, People daughter, People &son) : mother(mother), father(father), daughter(daughter), son(&son)
{
cout << mother.GetName() << ", " << father.GetName() << ", " << daughter.GetName() << " and " << son.GetName() << " is a family!" << endl;
}

/* 因为是People的友元类所以可以访问People的private成员 */
void ShowFamily() {
cout << "In the family, father is " << father.name << ", mother is " << mother.name << ", daughter is "\
<< daughter.name << ", son is " << son->name << endl;
}
private:
People mother, father, daughter, *son;
};

int main() {
People myself("小鲸鱼");/* Line 1 */
People myfather("father", Male, 39), mymother("mother", Female, 37);/* Line 2 */
People mybrother("brother", Male, 12);/* Line 3 */
People myfriend("小金鱼");/* Line 4 */
People::Showcount(); /* 静态成员函数 */
IsFriend(myself, myfriend);/* 友元函数 */
Family myfamily(mymother, myfather, myself, mybrother);/* Line 7 */
myfamily.ShowFamily();/* Line 8 */
People::Showcount(); /* 静态成员函数 */
return 0;/* Line 10 */
}
/* Output
* New People: 小鲸鱼 Girl 18
* 调用了委托构造函数 <- Line 1 调用委托构造函数初始化新建People对象
* New People: father Man 39
* New People: mother Girl 37 <- Line 2 新建两个People对象(从左向右)
* New People: brother Man 12 <- Line 3 新建一个People对象
* New People: 小金鱼 Girl 18
* 调用了委托构造函数 <- Line 4 新建一个People对象
* There are 5 People! <- Line 5 目前有5个People对象
* 小鲸鱼 and 小金鱼 are good friends! <- 调用友元函数
* Copy 小鲸鱼 ! <- Line 7 myself对象作为实参传递给Family的构造函数发生复制构造,另外三个对象是传递引用所以没有发生复制构造
* Copy mother !
* Copy father !
* Copy 小鲸鱼 ! <- Line 7 myfamily对象初始化时调用复制构造函数按对象内成员定义顺序初始化成员对象, mybrother是对象指针所以没有发生复制构造
* mother, father, 小鲸鱼 and brother is a family! <- myfamily对象初始化完成
* 小鲸鱼 has been delete! <- myfamily对象初始化完成, 作为构造函数形参的对象被删除时调用析构函数
* In the family, father is father, mother is mother, daughter is 小鲸鱼, son is brother <- Line 8
* There are 8 People! <- 之前的5个People对象+myfamily的3个People对象
* 小鲸鱼 has been delete! <- Line 10 程序运行结束,按照对象的定义顺序依次调用析构函数并删除对象
* father has been delete!
* mother has been delete!
* 小金鱼 has been delete!
* brother has been delete!
* mother has been delete!
* father has been delete!
* 小鲸鱼 has been delete!
*/

结构体

  • 和C语言的结构体类似,是一种特殊形态的类
  • 默认成员访问权限是public
  • 只有数据成员没有函数成员的情况适合使用结构体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* 结构体定义 */
struct 结构体名
{
public:
公有数据成员;
private:
私有型成员;
protected:
保护型成员;
};
/* 结构体变量声明(区别C语言,前面可以不用加struct) */
结构体名 结构体变量名;
/* 所有数据成员都是public, 没有自定义构造函数, 没有基类和虚函数,可以这样赋初值 */
结构体名 结构体变量名 = {数据成员1初值, 数据成员2初值, …… }

联合体

联合体: 成员共用一组内存空间,联合体内有且只有一个成员生效, 联合体变量声明类似结构体,不再赘述。

1
2
3
4
5
6
7
8
9
union 联合体名
{
public:
公有数据成员;
private:
私有型成员;
protected:
保护型成员;
};

数据共享和保护

标识符作用域

  • 函数原型作用域
    • 函数原型中的参数,仅在"()"内
  • 局部作用域
    • 函数形参、块中标识符,仅在块"{}"内
  • 类作用域
    • 类内和非内联成员函数的函数体
  • 文件作用域
    • 上述作用域之外的就是文件作用域,范围在声明到文件结尾
  • 命名空间作用域

可见性

  • 在作用域 \(\neq\) 可见
  • 同名标识符内层会覆盖外层使得外层不可见
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* Code language: C++ */
int func(int i) {
cout << &i << ends << i << endl;
if (true) {
int i = 1;
cout << &i << ends << i << endl;
}
cout << &i << ends << i << endl;
return i;
}

int main() {
func(10);
return 0;
}
/* Output
* 0032F640 10
* 0032F62C 1
* 0032F640 10
*/

对象的生存期

  • 静态生存期(static声明)
    • 与程序运行期相同
  • 动态生存期(作用域内)

共享数据的保护

对于又需要共享,又不希望被修改的数据应该声明为常类型(const),不改变对象状态的成员函数声明为常函数

常类型:必须初始化,不能修改
常对象只能调用常成员函数

外部变量、外部函数: extern关键字声明


数组、指针、字符串

基本是C语言的内容,只提一些注意的点和与C不同的点

数组

定义: 类型说明符 数组名[常量][常量]……;
数组名是表示数组首元素内存地址的常量(可以理解为常量指针)
二维数组按行存储,static默认初始化为0,部分初始化化剩下的自动初始化为0

指针

定义: (存储类型-static, const等) 数据类型 *指针名 = 初始地址;

指针空值\(nullptr\)(C++ 11), 比NULL更安全
void类型指针可以指向任何类型对象

指向常量的指针(const int *p;): 指向常量,指针本身可变
常量指针(int *const p): 指针不可变,不指向常量

指针数组(int *p[3]): 数组的元素是指针,这是一个数组
数组指针(int (*p)[3]): 指向数组的指针,这是一个指针("[]"的优先级比"*"高所以要用括号)
二维数组(int nums[3][3]): 数组的元素是数组,不太严谨的可以理解为是(常指针)数组

返回值是指针类型的函数:

  • 存储类型 数据类型 *函数名(){}
  • 注意:返回局部变量的地址是非法的

指向函数的指针:

  • 定义: 存储类型 数据类型 (*函数指针名)();
  • 例子: int (*func)(int, int) -> 指向有两个int形参且返回值是int的函数的指针

对象指针: 对象使用'.'访问成员,对象指针使用'->'访问成员,'this'指针指向当前对象自己

动态内存分配

动态内存申请:

  • 定义: new 类型名(初始化参数列表)
  • 定义: new 类型名[数组长度]
  • 定义: new 类型名[数组长度1][数组长度2][数组长度3]
  • 成功返回指向分配空间的对应类型的指针,失败抛出异常

释放内存

定义: delete 指针名;, 释放指针所指内存空间,指针必须是new返回值
定义: delete[] 指针名;, 释放指针所指内存空间,指针必须是new返回值(多维数组也只要一个'[]')

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Code language: C++ */
/* People类定义见上一代码示例 */
int main() {
People *p = new People("小鲸鱼", Female, 18);
p->ShowMyself();
delete p;
return 0;
}
/* Output
* New People: 小鲸鱼 Girl 18
* My name is 小鲸鱼
* I'm a Girl
* I'm 18 years old
*/

继承和派生

什么是继承与派生

  • 继承是保持已有类的特性以此来构造新类
  • 派生是在已有类的基础上新增自己的特性而产生新类
  • 被继承的已有类称为基类/父类
  • 派生出的新类称为派生类/子类
  • 直接参与派生的基类称为直接基类,否则是间接基类

单/多继承派生类:

1
2
3
4
class 派生类名 : 继承方式 基类名, 继承方式 基类名, ……
{
成员声明;
}

继承方式

继承方式影响派生类成员和对象对基类成员的访问权限

公有继承public

  • 基类的public和protected成员在派生类中保持不变
  • 基类的private成员不可直接访问(可以通过基类成员函数间接访问)
  • 派生类的成员函数可以访问基类的public和protected, 不能直接访问private
  • 在类外通过派生类对象只能访问基类的public成员

私有继承private

  • 基类的public和protect成员都作为派生类的private成员
  • 基类的private成员不可直接访问(可以通过基类成员函数间接访问)
  • 派生类的成员函数可以访问基类的public和protected, 不能直接访问private
  • 在类外通过派生类对象不能访问任何基类成员

保护继承protected

  • 基类的public和protect成员都作为派生类的protected成员
  • 基类的private成员不可直接访问(可以通过基类成员函数间接访问)
  • 派生类的成员函数可以访问基类的public和protected, 不能直接访问private
  • 在类外通过派生类对象不能访问任何基类成员

protected成员的特点与作用:

  • 对所在类对象模块与private成员性质相同
  • 对派生类对象模块与public成员性质相同

类的继承示例

在前文类的定义示例基础上增加了单继承类妻子wife和丈夫husband(这里仅列出妻子,其他代码见后文)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* Code language: C++ */
/* 单继承类 */
class Husband;
class Wife : public People
{
public:
/* 构造函数 */
/* using People::People; // 继承基类的构造函数 */
Wife() : Wife("Wife", 23, nullptr) {}
Wife(string myname) : Wife(myname, 23, nullptr) {}
Wife(string myname, int age) : Wife(myname, age, nullptr) {}
Wife(string myname, int myage, Husband *myhusband) : People(myname, Female, myage), husband(myhusband) {}
void ShowHusband();
private:
Husband *husband;
};

void Wife::ShowHusband() {
cout << this->GetName() << "'s husband is " << this->husband->GetName() << endl;
}

类型转换

  • 公有派生类对象可以被当做基类对象使用
  • 派生类对象可以隐含转换为基类对象
  • 派生类对象可以初始化基类引用
  • 派生类指针可以隐含转化为基类指针
  • 基类对象名、指针只能使用基类继承的成员

派生类的构造函数与析构函数

  • 派生类默认不继承基类的构造函数
  • C++11 规定可以使用using继承基类构造函数using 基类名::基类名
  • 派生类的构造函数需要给基类的构造函数传递参数并且初始化新增成员
  • 派生类构造函数语法(类外定义)
  • 派生类名::派生类名(形参列表) : 基类名(基类参数列表), 基类名(基类参数列表), 新增成员初始化列表{}
  • 执行顺序: 按继承顺序调用基类构造函数, 按定义顺序初始化新增成员, 函数体
  • 复制构造函数
    • 默认的复制构造函数,先调用基类复制构造函数,再复制新增成员
    • 可以将派生类的引用作为基类复制构造函数的实参(隐含类型转换), 再复制新增成员
  • 析构函数
    • 派生类不继承基类的析构函数,但系统会隐式调用基类的析构函数
    • 顺序: 先析构新增成员,在调用基类析构函数

派生类成员的访问与构成

  • 派生类默认继承基类除构造函数和析构函数外的所有成员
  • 建议不要重新定义继承的非虚函数

当派生类与基类有相同成员时,派生类成员会屏蔽基类成员,未限定情况下默认访问的是派生类成员,要访问被屏蔽的基类成员需要使用作用域操作符基类名::成员名来限定(同时可以避免多继承同名成员造成的二义性问题)#

虚基类

  • 作用: 防止多重继承可能导致的不一致性和冗余的问题
    • 多继承时可能对同一基类多次继承使得数据不一致
    • 为最远派生类提供唯一基类,防止产生多次复制
  • 声明: class 派生类 : virtual 继承方式 基类
  • 在第一继承时就要将基类设置为虚基类
  • 虚基类的构造函数
    • 建立对象时所指定的类为最远派生类
    • 虚基类的成员是由最远派生类的构造函数直接调用虚基类的构造函数进行初始化的
    • 建立对象时,只有最远派生类的构造函数调用虚基类的构造函数,其他派生类对虚基类的构造函数的调用忽略

类的继承综合示例

程序中去除了前面构造函数和析构函数的输出演示,并添加了单继承类妻子和丈夫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/* Code language: C++ */
/* 枚举类:性别(女、男、非二元性别) */
enum gender { Female, Male, Other };

class People
{
friend class Family;/* 声明Family为People的友元类 */
public:
People() = default;/* 保留隐含的默认构造函数 */
People(string myname, gender mysex, int myage);/* 构造函数的声明 */
/* 委托构造函数 */
People(string myname) :People(myname, Female, 18) {
// cout << "调用了委托构造函数" << endl;
}
People(const People &p); /* 复制构造函数 */
//People(const People &p) = delete; /* 禁用复制构造函数 */

/* 析构函数 */
~People() {
// cout << name << " has been delete!" <<endl;
--count;
}

/* 类的成员函数 */
void ShowMyself();
void SetName(string NewName) {
name = NewName;
}
string GetName() {
return this->name;
}

void SetAge(int myage) {
age = myage;
}

/* 类的静态成员函数 */
static void Showcount() {
cout << "There are " << count << " People!" << endl;
}

/* 友元函数 */
friend void IsFriend(const People &p, const People &q);
private:
/* 类的数据成员 */
string name = "Wu Ming";
gender sex = Female;
int age = 18;
/* 类的静态数据成员 */
static int count;
};

/* 初始化类的静态数据成员 */
int People::count = 0;

/* People类的构造函数定义(使用列表初始化成员) */
People::People(string myname, gender mysex, int myage) :name(myname), sex(mysex), age(myage) {
cout << "New People: " << myname << ends << (mysex == Female ? "Girl" : "Man") << ends << myage << endl;
++count;
}

/* 复制构造函数 */
People::People(const People &p) :name(p.name), sex(p.sex), age(p.age) {
// cout << "Copy " << name << " !" << endl;
++count;
}

void People::ShowMyself() {
cout << "My name is " << name << endl;
cout << "I'm a " << (sex == Female ? "Girl" : "Man") << endl;
cout << "I'm " << age << " years old" << endl;
}

/* People的友元函数,可以访问People中的private和protected成员 */
void IsFriend(const People &p, const People &q) {
if (p.age == q.age && p.sex == q.sex)
cout << p.name << " and " << q.name << " are good friends!" << endl;
}


/* 单继承类 */
class Husband;
class Wife : public People
{
public:
/* 构造函数 */
/* using People::People; // 继承基类的构造函数 */
Wife() : Wife("Wife", 23, nullptr) {}
Wife(string myname) : Wife(myname, 23, nullptr) {}
Wife(string myname, int age) : Wife(myname, age, nullptr) {}
Wife(string myname, int myage, Husband *myhusband) : People(myname, Female, myage), husband(myhusband) {}
void ShowHusband();
void SetHusband(Husband &h) { husband = &h; }
private:
Husband *husband;
};

class Husband : public People
{
public:
/* 构造函数 */
Husband() = default;
Husband(string myname) :Husband(myname, 25, nullptr) {}
Husband(string myname, int age) : Husband(myname, age, nullptr) {}
Husband(string myname, int myage, Wife *mywife) : People(myname, Male, myage), wife(mywife) {}
void ShowWife();
void SetWife(Wife &w) { wife = &w; }
private:
Wife *wife;
};

void Wife::ShowHusband() {
if (husband)
cout << this->GetName() << "'s husband is " << this->husband->GetName() << endl;
}

void Husband::ShowWife() {
if (wife)
cout << this->GetName() << "'s Wife is " << this->wife->GetName() << endl;
}

/* 组合类Family */
class Family
{
public:
/* 构造函数 */
Family(People &mother, People &father, People daughter, People &son) : mother(mother), father(father), daughter(daughter), son(&son)
{
// cout << mother.GetName() << ", " << father.GetName() << ", " << daughter.GetName() << " and " << son.GetName() << " is a family!" << endl;
}

/* 因为是People的友元类所以可以访问People的private成员 */
void ShowFamily() {
cout << "In the family, father is " << father.name << ", mother is " << mother.name << ", daughter is "\
<< daughter.name << ", son is " << son->name << endl;
}
private:
People mother, father, daughter, *son;
};

int main() {
/* 小鲸鱼 */
People myself("小鲸鱼", Female, 18);
/* 小鲸鱼的妈妈 */
Wife mymother("Mom", 37);
/* 小鲸鱼的爸爸 */
Husband myfather("Dad", 39, &mymother);
mymother.SetHusband(myfather);
/* 小鲸鱼的弟弟 */
People mybrother("鲸鱼宝宝", Male, 12);
/* 小鲸鱼的闺蜜 */
People myfriend("小金鱼", Female, 18);

/* 妈妈和爸爸的关系 */
mymother.ShowHusband();
myfather.ShowWife();

People::Showcount(); /* 静态成员函数 */

IsFriend(myself, myfriend);/* 友元函数 */

Family myfamily(mymother, myfather, myself, mybrother);
myfamily.ShowFamily();

People::Showcount(); /* 静态成员函数 */
}
/* Output
* New People: 小鲸鱼 Girl 18
* New People: Mom Girl 37
* New People: Dad Man 39
* New People: 鲸鱼宝宝 Man 12
* New People: 小金鱼 Girl 18
* Mom's husband is Dad
* Dad's Wife is Mom
* There are 5 People!
* 小鲸鱼 and 小金鱼 are good friends!
* In the family, father is Dad, mother is Mom, daughter is 小鲸鱼, son is 鲸鱼宝宝
* There are 8 People!
*/

多态

运算符重载

  • 运算符重载是对已有的运算符赋予多重含义,使得同一运算符作用与不同类型的数据时导致不同的行为(类似函数重载)
  • 运算符可以重载除 . .* :: ?.以外所有C++中已有的运算符
  • 重载的运算符结合性和优先级不变
  • 运算符重载可以重载为类的非静态成员函数或非成员函数

运算符重载为成员函数

定义:

1
2
3
/* Code language: C++ */
函数类型 operator 运算符 (形参){}
/* 参数个数 = 原操作数个数 - 1 (除去参与运算的对象本身)(后置++ --除外) */
  • 双目运算符重载: 类1 op 类2, 则运算符op应该重载为类A的成员函数, 形参是类2(要求类1必须是自定义类型,类2可以是基本数据类型), 重载后类1 op 类2相当于调用重载函数类A.operator op(类2)
  • 前置单目运算符重载: 无形参,重载后op 类相当于调用重载函数类.operator op()
  • 后置单目运算符重载: 为与前置区分,必须有一个int类型形参,但不用写参数名、函数体内也不能使用该参数,重载后类 op相当于调用重载函数类.operator op(int)

运算符重载为非成员函数

  • 有些运算符不能重载为成员函数,例如基本数据类型,或是不能重载运算符的对象
  • 函数的形参代表自左向右的操作数
  • 参数个数 = 原操作数个数(后置++ --除外)
  • 至少要有一个自定义类型,无法重载已有的运算
  • 其他和重载为成员函数类似

运算符重载示例

程序中删除了组合类Family,并为People, Wife, Husband类添加了运算符重载,并且People类还添加了移动构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
/* Code language: C++ */
/* 枚举类:性别(女、男、其他) */
enum gender
{
Female,
Male,
Other
};

class People
{
friend class Family; /* 声明Family为People的友元类 */
public:
People() = default; /* 保留隐含的默认构造函数 */
People(string myname, gender mysex, int myage); /* 构造函数的声明 */
/* 委托构造函数 */
People(string myname) : People(myname, Female, 18)
{
// cout << "调用了委托构造函数" << endl;
}
People(const People &p); /* 复制构造函数 */
//People(const People &p) = delete; /* 禁用复制构造函数 */

/* 移动构造函数 */
People(People &&p) : name(p.name), sex(p.sex), age(p.age)
{
cout << "调用了移动构造函数" << endl;
}
/* 移动赋值函数(运算符重载函数) */
void operator=(People &&p)
{
cout << "调用了移动赋值函数" << endl;
p.ShowMyself();
this->name = p.name;
this->age = p.age;
this->sex = p.sex;
}

/* 前置单目运算符重载 */
People operator++()
{
this->age++;
return *this;
}

/* 后置单目运算符重载 */
People operator++(int)
{
People tmp(*this);
this->age++;
return tmp;
}

/* 析构函数 */
~People()
{
// cout << name << " has been delete!" << endl;
--count;
}

/* 类的成员函数 */
void ShowMyself();
void SetName(string NewName)
{
name = NewName;
}
string GetName()
{
return this->name;
}

void SetAge(int myage)
{
age = myage;
}

/* 类的静态成员函数 */
static void Showcount()
{
cout << "There are " << count << " People!" << endl;
}

/* 友元函数 */
friend void IsFriend(const People &p, const People &q);
friend ostream &operator<<(ostream &out, People &p);

private:
/* 类的数据成员 */
string name = "Wu Ming";
gender sex = Female;
int age = 18;
/* 类的静态数据成员 */
static int count;
};

/* 初始化类的静态数据成员 */
int People::count = 0;

/* People类的构造函数定义(使用列表初始化成员) */
People::People(string myname, gender mysex, int myage) : name(myname), sex(mysex), age(myage)
{
// cout << "New People: " << myname << ends << (mysex == Female ? "Girl" : "Man") << ends << myage << endl;
++count;
}

/* 复制构造函数 */
People::People(const People &p) : name(p.name), sex(p.sex), age(p.age)
{
// cout << "Copy " << name << " !" << endl;
++count;
}

void People::ShowMyself()
{
cout << "My name is " << name << endl;
cout << "I'm a " << (sex == Female ? "Girl" : "Man") << endl;
cout << "I'm " << age << " years old" << endl;
}

/* People的友元函数,可以访问People中的private和protected成员 */
void IsFriend(const People &p, const People &q)
{
if (p.age == q.age && p.sex == q.sex)
cout << p.name << " and " << q.name << " are good friends!" << endl;
}

/* People的友元函数,重载<<为非成员函数 */
ostream &operator<<(ostream &out, People &p)
{
out << "I'm a " << (p.sex == Female ? "Girl" : "Man") << " named " << p.name << ", " << p.age << " years old.";
return out;
}

/* 单继承类 */
class Husband;
class Wife : public People
{
friend class Husband; /* 声明Husband为Wife的友元类 */
public:
/* 构造函数 */
/* using People::People; // 继承基类的构造函数 */
Wife() : Wife("Wife", 23, nullptr) {}
Wife(string myname) : Wife(myname, 23, nullptr) {}
Wife(string myname, int age) : Wife(myname, age, nullptr) {}
Wife(string myname, int myage, Husband *myhusband) : People(myname, Female, myage), husband(myhusband) {}
/* 成员函数 */
void ShowHusband();
void SetHusband(Husband &h) { husband = &h; }

/* 运算符重载 */
People operator+(Husband &H);

private:
Husband *husband;
};

class Husband : public People
{
friend class Wife; /* 声明Wife为Husband的友元类 */

public:
/* 构造函数 */
Husband() = default;
Husband(string myname) : Husband(myname, 25, nullptr) {}
Husband(string myname, int age) : Husband(myname, age, nullptr) {}
Husband(string myname, int myage, Wife *mywife) : People(myname, Male, myage), wife(mywife) {}
void ShowWife();
void SetWife(Wife &w) { wife = &w; }

/* 运算符重载函数 */
People operator+(Wife &w);

private:
Wife *wife;
};

void Wife::ShowHusband()
{
if (husband)
cout << this->GetName() << "'s husband is " << this->husband->GetName() << endl;
}

void Husband::ShowWife()
{
if (wife)
cout << this->GetName() << "'s Wife is " << this->wife->GetName() << endl;
}

/* 运算符重载函数 */
People Wife::operator+(Husband &H)
{
return People(this->GetName() + "and" + H.GetName() + "'s daughter", Female, 0);
}

/* 运算符重载函数 */
People Husband::operator+(Wife &w)
{
return People(this->GetName() + "and" + w.GetName() + "'s son", Male, 0);
}

int main()
{
/* 小鲸鱼 */
People myself("小鲸鱼", Female, 18);
/* 小鲸鱼的妈妈 */
Wife mymother("Mom", 37);
/* 小鲸鱼的爸爸 */
Husband myfather("Dad", 39, &mymother);
mymother.SetHusband(myfather);

/* 这里挺迷惑的,直接在声明时赋初值那么返回的就是运算符重载函数中声明的那个类
* 而声明时不赋初值而是在下一行再赋值则会调用赋值运算符重载函数(并且这个隐藏函数会因为定义移动构造函数而被删除),为什么赋值调用的不是复制构造函数呢?
*/
People princess = mymother + myfather;
People prince = myfather + mymother;
// prince = myfather + mymother;

cout << princess << endl;
cout << prince << endl;

/* 长大一岁啦~ */
cout << myself << endl;
cout << "One year later …… " << endl;
++myself;
cout << myself << endl;

return 0;
}
/* Output
* I'm a Girl named MomandDad's daughter, 0 years old.
* I'm a Man named DadandMom's son, 0 years old.
* I'm a Girl named 小鲸鱼, 18 years old.
* One year later ……
* I'm a Girl named 小鲸鱼, 19 years old.
*/

虚函数

  • 虚函数是动态绑定的函数,即编译阶段不进行静态绑定,而是运行过程动态绑定
  • 声明: virtual 函数类型 函数名(形参表) { }
  • 虚函数是实现多态性的基础
  • 虚函数必须是非静态的成员函数,构造函数不能是虚函数,但析构函数可以是虚函数
  • 派生类可以不显示声明virtual, 编译器根据函数原型自动判断并覆盖基类虚函数
  • 派生类的虚函数会覆盖基类中所有同名函数的重载形式
  • 虚析构函数: 允许其它函数通过基类指针调用对象的析构函数时就要将基类析构函数设为虚函数

虚表

虚函数的动态绑定是通过虚表来实现的
每个多态类都有一张虚表,虚表中存放了各个虚函数的入口地址,每个对象都有一个虚表指针指向虚表(构造函数中初始化)
通过多态类调用虚函数时会通过虚指针找到虚表,进而找到虚函数入口地址进而正确的调用虚函数

抽象类

  • 纯虚函数是在基类中声明的,但由于类信息不够具体无法实现的虚函数
  • 纯虚函数声明: virtual 函数类型 函数名(参数表) = 0;其含义是没有具体实现
  • 带有纯虚函数的类称为抽象类,因为没有具体实现,所以抽象类无法实例化,只能作为基类

override和final

override 是用于派生类的虚函数的声明,用途是指示编译器覆盖基类的虚函数,若没有成功覆盖则报错,避免因为疏漏导致的错误

final 是拒绝类被继承或函数被重新的声明

1
2
3
4
5
6
7
8
9
10
11
/* Code language: C++ */
/* A类无法继承,无法派生出新类 */
class A final{
int i;
}

/* B的func成员函数继承后无法覆盖重写 */
class B{
int j;
virtual void func() final;
}

模板

函数模板

创建通用功能的函数,支持多种不同形参

定义

1
2
template <模板参数表>
函数定义

其中模板参数表为类型参数(class/typename 标识符)、常量参数(类型说明符 标识符)、模板参数(template<参数表>class 标识符)

1
2
3
4
5
6
/* Code language: C++ */
template <typename T>
void func(T s)
{
cout << s << endl;
}

类模板

类模板定义

1
2
3
4
5
6
7
template <模板参数表>
class 类名
{类成员声明};

/* 类外定义的成员函数 */
template <模板参数表>
函数类型 类名<模板参数标识符列表>::函数名(参数表){}

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/* Code language: C++ */
/* 类模板 */
template <int T, class U, typename V>
class class_name
{
public:
void Show();

private:
int s = T;
U member1;
V member2;
};

/* 类模板的成员函数 */
template <int T, class U, typename V>
void class_name<T, U, V>::Show()
{
cout << this->s << ends << this->member1 << ends << this->member2 << endl;
}

int main()
{
class_name<1, People, int> c; /* People类定义见前面示例 */
c.Show();
return 0;
}
/* Output
* 1 I'm a Girl named Wu Ming, 18 years old. 8
*/

异常处理

定义

1
2
3
4
5
6
7
/* 抛出异常 */
throw 表达式;
/* 异常处理 */
try{保护代码}
catch(异常类型){异常处理}
catch(异常类型){异常处理}
catch(...){异常处理}/* 能捕获任何异常 */

函数的异常声明

为了增强程序的可读性和可维护性,使程序员在使用一个函数时就能看出这个函数可能会拋出哪些异常,C++ 允许在函数声明和定义时,加上它所能拋出的异常的列表

1
void func() throw (int, double, A, B, C);

据说C++的异常处理不是特别好,所以还是尽量避免错误吧


泛型

  • 概念
    • comparable: 可比较的
    • assignbale: 可赋值的
    • sortable: comparable and assignbale
  • 泛型: 指编写不依赖于具体类型的程序,将算法从特点数据结构中抽象出来,基础是模板

这东西挺复杂的还是另外开一篇文章(点击查看)写吧~

--- ♥ end ♥ ---

欢迎关注我呀~