一、绪论

C++是C语言的拓充,C++包含了C所有的属性,换一句话说,C语言在C++中都合法。

C++的编程思想:面向对象,(半面向对象,半面向过程)

可以说在C++\中一切皆对象

C++的三大属性:封装继承多态

C++对C的兼容

1> C语言文件

编写一个C语言文件:XXX.c 编译:gcc XXX.c

2> C++语言文件

编写一个C++文件:XXX.cpp 编译:g++ XXX.cpp

3> 头文件

C: 以.h结尾的文件 eg:#include<stdio.h>

C++: 不以.h结尾 eg:#include

二、第一条语句

cout

cout是输出类的类对象,具有输出功能,可以自动识别数据类型,无需加格式符。

<< 插入符(输出符)

endl :换行

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
#include <iostream>
//# 预处理
//inclue 包含 包括
//iostream 标准的输入输出流 istream标准输入流 ostream标准输出流


using namespace std;
//using 使用命名空间的关键字
//namespace 命名空间的关键字
//std 标准的命名空间


//程序入口
int main()
{//程序的开始


cout << "Hello World!" << endl;


cout << "今天是我学习C++的第一天,好开心~" << endl << 10 << endl;


return 0;
}//程序的结束

试编程

1
2
3
4
*
**
***
****
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>


using namespace std;


int main()
{
for(int i=0; i<10; i++)
{
int j=0;
for(; j<=i; j++)
{
cout << "*";
}
cout << endl;
}
}

cin

cin 是输入类的类对象,具有输入功能,可以自动识别数据类型,无需加格式符

>> 提取符(输入符)

不需要给变量加地址符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>

using namespace std;

int main()
{
cout << "请输入你的年纪:" << endl;

int age;

cin >> age;

cout << "age = " << age << endl;
return 0;
}

三、C++中的数据类型

C++的数据类型:基本数据类型、构造数据类型

基本数据类型:char、short、int、long、float、double、bool、string

构造数据类型:数组、指针、结构体、共用体、枚举、类

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
#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
//字符型 每个字符都有对应的ASCII码值
char a = 'A';
cout << "a = " << a << endl;
cout << "a = " << int(a) << endl;
cout << "=============================" << endl;

//整型 进制 有无前缀的问题
// 二进制 0b
// 八进制 0
// 十进制 无
// 十六进制 0x
int b=100, b2=0b100, b3=0100, b4=0x100;
cout << b << endl;
cout << oct << b << endl; //oct八进制输出
cout << dec << b << endl; //dec十进制输出
cout << hex << b << endl; //hex十六进制输出
cout << b << endl; //如果改变了输出的进制,则后面都以这种进制输出
cout << dec << b << endl;
cout << "=============================" << endl;



//浮点型
double d1 = 12.3456789, d2=1.23456789;
cout << d1 << endl; //保留有效位数6位,存在四舍五入
cout << d2 << endl;
//如果想改变有效位数,需要用一个方法,该方法(函数)在iomanip头文件中
cout << setprecision(4) << d1 << endl; //保留有效位数
cout << setprecision(5) << fixed << d1 << endl; //保留小数点后几位

return 0;
}

1. 字符串类型的初始化和赋值

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
#include <iostream>

using namespace std;

int main()
{
string str1; // == int a 无参构造函数

string str2 = "hello world"; //定义变量的同时赋值,初始化
cout << str2 << endl;

string str3 = str2; //将str2的值赋值给str3
cout << str3 << endl;

string str4(str3); //str4 = str3 有参构造函数
cout << str4 << endl;

str1 = "hello kitty"; //赋值操作

string str5(str1,3); //将str1从下标为3的地方开始赋值给str5
cout << str5 << endl;

char *p = (char *)"abcdfdjgk"; //string 类型和char *不是一种类型

return 0;
}

2. 字符串中常用的函数

size(): 元素个数

empty(): //判断字符串是否为空,如果为空放回true,否则false

capacity(): //计算容量大小

1
2
3
4
5
6
if(!str5.empty())
{
//输出元素个数
cout << str5.size() << endl;
cout << str5.capacity() << endl;
}

3. C++中的字符串和C语言中字符串的风格互换

1> C语言风格的字符串可以直接转换成C++风格的字符串

2> C++风格的字符串不可以直接转换成C语言风格的字符串

1
2
3
4
5
6
7
8
9
10
11
char a[20] = "world";
string b = "hello";


b = a;
cout << b << endl;

string c = "kitty";
//a = c;错误,a是常量 常量不可以赋值操作
strcpy(a, c.c_str()); //c_str() 将c++风格的字符串转换成C语言风格的字符串

4. 字符串元素的访问

1> 下标 -------->不判断是否越界

2> at() ----------->判断是否越界

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include<string.h>


using namespace std;


int main()
{

string c2 = "abde";
cout << c2[6] << endl;


cout << c2.at(2) << endl;
cout << c2.at(8) << endl;
return 0;
}

5. 字符串的比较

由于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
26
27
28
29
30
#include <iostream>


using namespace std;

int main()
{
string str1="abc";
string str2="ABCDEFGHIJKKKKKKKKKKKKKK";


if(str1!=str2)
{
if(str1>str2)
{
cout << "str1 > str2" << endl;
}
else
{
cout << "str1 < str2" << endl;
}
}
else
{
cout << "str1 == str2" << endl;
}
return 0;
}


6. 字符串的输入

1
2
3
4
5
6
7
8
9
10
string userName;
//cin >> userName; //cin输入字符串 以空格作为结束
//cout << userName << endl;


getline(cin,userName); //包含空格


cout << userName << endl;

四、C++数组array

需要包含头文件 #include

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
#include <iostream>
#include<array> // 容器

using namespace std;

int main()
{
// int a[5];
// for(int i=0; i<5; i++)
// {
// //cin >> a[i];
// cin >> *(a+i);
// }
// for(int i=0; i<5; i++)
// {
// cout << a[i] << " ";
// }
// cout << endl;


// array<int,5> a; // === int a[5]
// array<int,5>::iterator iter; //定义了一个迭代器
// for(iter = a.begin(); iter != a.end(); iter++)
// //begin()第一个元素的地址 end()表示最后一个元素的下一个元素的地址
// {
// cin >> *iter;
// }
// for(iter = a.begin(); iter != a.end(); iter++)
// {
// cout << *iter << " ";
// }


// cout << endl;


// int a[3][2];
// for(int i=0; i<3; i++)
// {
// for(int j=0; j<2; j++)
// {
// cin >> a[i][j];
// }
// }
// for(int i=0; i<3; i++)
// {
// for(int j=0; j<2; j++)
// {
// cout << *(*(a+i)+j) << " ";
// }
// cout << endl;
// }


array< array<int,2>, 3> a; // === int a[3][2]
array< array<int,2>, 3>::iterator iter1; //迭代器 遍历每一个一维数组
array<int, 2>::iterator iter2; //遍历一维数组的每一个元素
for(iter1 = a.begin(); iter1 != a.end(); iter1++)
{
for(iter2 = (*iter1).begin(); iter2 != (*iter1).end(); iter2++)
{
cin >> *iter2;
}
}
for(iter1 = a.begin(); iter1 != a.end(); iter1++)
{
for(iter2 = (*iter1).begin(); iter2 != (*iter1).end(); iter2++)
{
cout << *iter2 << " ";
}
cout << endl;
}


return 0;
}


五、命名空间

1> 多人协同开发,避免命名污染(作用)

六、引用

1概念

引用就是个别名。

2 格式

数据类型 &引用名 = 同类型的变量名 (& 引用符号)

1
2
3
4
数据类型 &引用名 = 同类型的变量名
eg:
int a = 10;
int &b = a; //b引用a,或者给a变量取个别名叫b

案例

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
#include <iostream>

using namespace std;

int main()
{
int a = 10, b = 20;
cout << "a = " << a << endl;


int &aa = a; //给a变量取个别名叫aa
cout << "aa = " << aa << endl;


aa = 30;//引用的改动,那就是对目标的改动
cout << "a = " << a << endl;


cout << "&a = " << &a << endl;
cout << "&aa = " << &aa << endl;

int *p; //指针可以先定义 后指向
p = &a;

//int &bb; 引用必须定义的同时初始化 指定目标
//bb = b;

p = &b;

//int &aa = b; 引用一旦指针目标,就不可以改变了





return 0;
}


img

3 数组的引用

3.1 概念

给数组取个别名

3.2 实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>

using namespace std;

int main()
{
int a[5] = {1,2,3,4,5};

//数组指针
int (*p)[5] = &a;

//数组引用
int (&b)[5] = a;

cout << a[3] << endl;
cout << b[3] << endl; // b[3] == a[3]

b[4] = 99;
cout << a[4] << endl;

return 0;
}

4 函数的引用

4.1 概念

给函数取个别名

4.2 实例

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
#include <iostream>

using namespace std;

int max(int x, int y)
{
return x>y?x:y;
}

int main()
{
//函数指针
int (*p)(int, int) = max;
cout << p(3,5) << endl;


//函数引用
int (&fun)(int, int) = max;
cout << fun(6,8) << endl; // fun(6,8) == max(6, 8)


return 0;
}


5 结构体中有引用成员

当结构体中有引用成员的话,使用该结构体类型定义变量时,就必须定义的同时初始化,否则报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>


using namespace std;


//定义一个学生结构体类型
struct student
{
int &id;
string name;
};


int main()
{
int num = 1001;
struct student stu1 = {num, "张三"}; //当结构体有引用成员时,必须定义的时候初始化

return 0;
}

6 当引用作为函数的返回值

1> 要求返回的变量的生命周期要长。

2> 静态局部变量或者在堆区空间申请的变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>

using namespace std;

int &fun() // int &num = num
{
static int num = 10;
return num;
}


int main()
{
int a;


a = fun(); // a = num
cout << a << endl;


return 0;
}


7 传值、传址、传引用

1> 传值,一定不会改变目标的值。

2> 传址,可能会改变目标的值,具体看代码设计。

3> 传引用,可能会改变目标的值,具体看代码设计。

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
#include <iostream>


using namespace std;


void fun(int a, int b) //int a = a
{
a++;
b++;
}
void fun2(int *a, int *b)//int *a = &a
{
*a++;
*b++;
}
void fun3(int &a, int &b)// int &a = a
{
int c = a;
}
int main()
{
int a = 10, b = 20;
fun(a,b);//值传递
cout << "main: a = " << a << " b = " << b << endl;

fun2(&a, &b);//址传递
cout << "main: a = " << a << " b = " << b << endl;

fun3(a,b);//引用传递
cout << "main: a = " << a << " b = " << b << endl;

return 0;
}

总结:指针与引用的区别(笔试、面试重点)

指针:存放地址的变量

引用:引用就是个别名

1> 指针可以先定义后指向,而引用必须定义的同时初始化。

2> 指针后期可以改变指向,而引用一旦指定目标,就不能发生变化。

3> 指针定义需要申请空间,而引用不需要申请空间。

4> 有指针数组,没有引用数组(原因:引用不是数据类型

5> 有二级指针,没有二级引用(原因:引用不是数据类型)

七、const

修饰变量时,表示该变量是常变量,其值不能被修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int *p;
int * const p; //指向不可变,指向里的值可变
int const *p; //指向可变,指向里的值不可变
int const * const p; //都不可变

int a = 10;
int const b = 20; // const int b = 20
int *pa = &a; //合法
int *pb = &b; //不合法
int const *pbb = &b; //可以 (int cosnt * const p)


int a = 10
int const b = 20;
int &ppa = a; //合法
int &ppb = b; //不合法
int const &pppb = b; //合法 int const &pppb ---->常引用

八、函数重载

1 概念

在同一个作用域下,两个以上的函数,取相同的函数名,其形参的参数个数或者参数类型不同,编译器会根据实参的参数个数或类型,自动调用对应的函数,这就是函数重载。

注意:不以返回值作为标准

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
#include <iostream>

using namespace std;

int fun()
{
return 5+5;
}


int fun(int x)
{
return x+10;
}
int fun(int x, int y)
{
return x+y;
}
int fun(int x, string y)
{
return x;
}
int main()
{
cout << fun() << endl;
cout << fun(10) << endl;




return 0;
}


试编程:

用函数重载实现不同数据类型之和

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
#include <iostream>


using namespace std;


int add(int x, int y)
{
return x+y;
}

int add(char x, char y)
{
return x+y;
}


string add(string x, string y)
{
return x+y;
}


double add(double x, double y)
{
return x+y;
}


//bool add(bool a, bool b)
//{
// return a+b;
//}


int main()
{
cout << add("hello","world") << endl;
cout << add(12.34, 45.7) << endl;
return 0;
}


2 默认参数的函数定义和使用

1
2
3
4
void fun(string name = "zhangsan")
{
cout << name << endl;
}

3 哑元(了解)

定义函数的时候,只定义类型,不定义形参名,在函数中也不使用。

作用:没有作用,占位

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
#include <iostream>

using namespace std;

int fun(int m, int ,int k)
{
return m+k;
}


int main()
{
cout << fun(1,2,3) << endl;

cout << fun(1,2,3) << endl;

cout << fun(1,2,3) << endl;

cout << fun(1,2,3) << endl;

cout << fun(1,2,3) << endl;

cout << fun(1,2,3) << endl;
return 0;
}


4内联函数

内联函数就是在函数定义前 加 inline 关键字

要求:

1> 内联函数体积要小

2> 一般代码不超过5行

3> 不能有复杂的语句,比如循环,递归

作用:提高代码的运行效率

内联函数和带参宏替换的区别:

1> 内联函数是函数调用,带参宏替换是替换

2> 内联函数是在编译的时候展开,带参宏替换是在预处理展开。

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
#include <iostream>

using namespace std;

#define MAX(x,y) x>y?x:y

inline int max(int x, int y) //内联函数
{
return x>y?x:y;
}

int main()
{
int m = 1;
int n = 1;
int c = max(++m,n);
cout << "m = " << m << " n = " << n << " c = " << c << endl;

m = 1;
n = 1;
c = MAX(++m,n);
cout << "m = " << m << " n = " << n << " c = " << c << endl;

return 0;
}

九、C++中的结构体

C语言中的结构体和C++中结构体的区别

1> C语言中的结构体在C++中依然适用。

2> C++中的结构体可以有函数,而C语言不可以。

3> C++中的结构体可以给变量赋初始值,而C语言中不可以。

4> C++中的结构体在定义结构体变量时,可以省略关键字struct不写,而C语言中不可以。

5> C++中的结构体中有访问权限,而C语言中的结构体没有访问权限。

6> C++中的结构体有特殊的成员函数,而C语言中的结构体没有。

7> C++中的结构体存在继承,而C语言中的结构体没有继承。

C++中的结构名一般首字母大写。

C++结构体的默认访问权限是共有的,—>public

访问权限:private: 私有权限 protected:保护权限 public:共有权限

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
#include <iostream>

using namespace std;

struct Student
{
int id = 1001; //C++中的结构体可以给变量赋初始值
string name;
double score;

void show() //C++中的结构体可以有函数
{
cout << "姓名: " << name << endl;
cout << "学号:" << id << endl;
cout << "成绩:" << score << endl;
}
};


int main()
{
//struct Student s1;
Student s1; //C++中的结构体可以省略关键字struct不写
s1.name = "张三";
s1.score = 99;


s1.show(); //调用内部成员函数








return 0;
}


十、类

1 相关概念

一般又有变量又有函数的构造数据类型,有类来完成,C++中的类是由C++中的结构体演变而来,只是默认访问权限默认继承方式以及关键字不同。

默认访问权限: private 私有的

默认继承方式:private 私有的

关键字:class

类内都可以成为成员,成员可以分为数据成员、成员函数

2 格式

1
2
3
4
5
6
7
8
9
class 类名
{
public:
公共的数据成员、成员函数
protected:
受保护的数据成员、成员函数
private:
私有的数据成员、成员函数
};

3 访问权限的介绍

public: 该权限是公共权限,表示该权限下的属性(变量)、方法(函数),可以在类内、子类、类外被访问。

protected: 该权限是受保护权限,表该权限下的属性(变量)、方法(函数),可以在类内、子类被访问,类外不可以被访问。

private: 该权限是私有权限,表示该权限下的属性(变量)、方法(函数),只能在类内被访问,子类、类外不可以被访问。

4 封装

类的三大属性:封装、继承、多态

封装就是将数据和对数据的处理捆绑在一起的过程,称为封装。

属性(变量)+ 方法(函数)

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
#include <iostream>


using namespace std;


class Stu //封装了一学生这样的类
{
public:
int id;
protected:
string name;
private:
double score;


public:
void show()
{
cout << id << endl; //在类内可以访问公共的成员
cout << name << endl; //在类内可以访问受保护的成员
cout << score << endl;//在类内可以访问私有的成员
}


void inint(int i, string n, double s)
{
id = i;
name = n;
score = s;
}


};


int main()
{
Stu s1; //用Stu这样的类 实例化了一个对象s1
s1.id = 1001; //在类外可以访问公共的成员
//s1.name = "张三";//在类外不可以访问受保护的成员
//s1.score = 99;//在类外不可以访问私有的成员


s1.inint(1002,"张三",99);
s1.show();


return 0;
}


十一、C++中动态内存分配和回收(堆区)

1> C语言中动态内存分配和回收使用的是malloc和free函数。

2> C++依然可以使用上述两个函数来完成。

3> C++也为用户提供了两个关键字new、delete来进行动态内存分配和回收。

1分配

1.1单个内存分配

格式: 数据类型 *指针名 = new 数据类型

1
2
3
eg:    int *p1 = new  int;

//在堆区空间申请了int类型大小的空间,并将该空间的地址赋值给p1

1.2连续内存分配

格式: 数据类型 *指针名 = new 数据类型[个数]

1
2
3
eg:  int *p2 = new int[5];

//在堆区空间连续申请了5个int类型大小的空间

2回收

2.1单个内存回收(释放)

格式:delete 指针名;

eg: delete p1;

2.2连续内存回收

格式:delete []指针名

eg: delete []p2;

实例:

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
#include <iostream>

using namespace std;

int main()
{
//在堆区空间申请了int类型大小的空间
int *p1 = new int;
cout << *p1 << endl;


//在堆区空间申请了int类型大小的空间,并且初始化了数值为10
int *p2 = new int(10);
cout << *p2 << endl;


//在堆区空间申请了char类型大小的空间,并且初始化了数值为A
char *p3 = new char('A');
cout << *p3 << endl;


cout << "=====================" << endl;


//在堆区空间连续申请了5个int类型大小的空间
int *p4 = new int[5];
for(int i=0; i<5; i++)
{
cout << p4[i] << endl;
}


cout << "=====================" << endl;
//在堆区空间连续申请了5个int类型大小的空间,并且初始化
int *p5 = new int[5]{100,200,300,400,500};
for(int i=0; i<5; i++)
{
cout << p5[i] << endl;
}

//单个内存回收
delete p1;
delete p2;
delete p3;


//连续内存回收
delete []p4;
delete []p5;

//避免野指针
p1 = nullptr;
p2 = nullptr;
p3 = nullptr;
p4 = nullptr;
p5 = nullptr;

return 0;
}

img

3 new、delete和malloc、free之间的区别(面试、笔试)

1> new、delete是关键字,而malloc、free是函数

2> new申请空间时可以初始化,而malloc不可以。

3> new申请空间以数据类型为单位,而malloc以字节为单位。

4> new申请空间,指针是什么类型,new申请就是什么类型,而malloc申请空间后需要强转才能使用(原因返回是void *)。

5> new、delete区分单个和连续的格式,而malloc、free不区分。

6> new申请对象空间时,会自动调用构造函数,而malloc不会。

7> delete释放对象空间时,会自动调用析构函数,而free不会。

十二、类中特殊的成员函数(重中之重)

1>特殊成员函数的种类:构造函数、析构函数、拷贝构造函数、拷贝赋值函数、移动赋值、移动拷贝、取地址符运算符重载、常取地址符运算符重载

2>特殊原因:

1)这些函数都是系统默认提供,无需程序员手动定义,如果程序员手动定义,则系统取消默认提供。

2)这些函数无需手动调用,在特定的情况下,系统自动调用,即使是程序员手动定义的函数。

1 构造函数

1.1功能

在给类实例化对象时,会自动调用构造函数来给类对象申请空间以及初始化使用。

1.2 格式

函数名:与类同名

返回值:无返回值 也无void

参数:可以无参数,可以有参数

访问权限:一般为public

1
2
3
4
类名(形参列表)
{
函数体内容;
}

1.3调用时机

实例化对象时,就会自动调用构造函数

1>栈区

何时实例化对象,何时自动调用构造函数

2>堆区

何时使用new, 何时自动调用构造函数

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
#include <iostream>

using namespace std;

//封装学生类
class Stu
{
private:
string name;
int age;
public:
//无参构造函数 是系统默认提供的
Stu()
{
cout << "无参构造函数" << endl;
}
//有参构造函数
Stu(string name, int age)
{
this->name = name;
this->age = age;
cout << "Stu::有参构造函数" << endl;
}

void show()
{
cout << "姓名:" << name << endl;
cout << "年纪:" << age << endl;
}

};

int main()
{
Stu s1("张三",19); //实例化一个s1对象 自动调用有参构造函数

Stu s2; //实例化一个s2对象,自动调用无参构造函数

return 0;
}
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
#include <iostream>

using namespace std;

//封装学生类
class Stu
{
private:
string name;
int age;
public:
//无参构造函数 是系统默认提供的
Stu()
{
cout << "无参构造函数" << endl;
}
//有参构造函数
Stu(string name, int age)
{
this->name = name;
this->age = age;
cout << "Stu::有参构造函数" << endl;
}


void show()
{
cout << "姓名:" << name << endl;
cout << "年纪:" << age << endl;
}


};

int main()
{
Stu s1("张三",19); //实例化一个s1对象 自动调用有参构造函数


Stu s2; //实例化一个s2对象,自动调用无参构造函数


cout << "==============================" << endl;


Stu *p = new Stu;


Stu *p2 = new Stu("王二麻",99);
p2->show();

cout << "==============================" << endl;

Stu *p3 = (Stu *)malloc(sizeof(Stu));

return 0;
}

img

注意:

1> 类中,系统都会提供一个默认无参数的构造函数,如果程序员手动定义构造函数,则系统取消提供的无参构造函数,如果后期想使用无参构造函数,则需要将无参构造函数显性定义出来,否则报错。

2> 可以在构造函数中设置默认参数值。

1.4 初始化列表

构造函数的本身工作是给类对象申请空间的,而初始化工作是由初始化列表来完成。

初始化列表格式:

初始化列表是由构造函数形参小括号后面由冒号引出。

1
2
3
4
类名(形参1,形参2,形参3):成员变量1(形参1),成员变量2(形参2),成员变量3(形参3)
{
函数体内容;
}

注意:初始化列表只有构造函数才有,其他普通函数没有。

1
2
3
4
5
6
7
8
    //有参构造函数
Stu(string name, int age):name(name),age(age) //初始化列表完成初始化操作
//string name = name
{
// this->name = name; 赋值操作
// this->age = age;
cout << "Stu::有参构造函数" << endl;
}

必须使用初始化列表的情况:

1>当类中有引用成员时,对该成员的初始化必须使用初始化列表。

2>当类中有常成员变量时,对该成员的初始化必须使用初始化列表来完成。

3>当类中嵌套另一个类对象时,对该对象的初始化,必须使用初始化列表来完成。

结论:

当类中嵌套另一对象时,调用构造函数的顺序:

1>先调用成员的构造函数,再调用自己的构造函数。

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
#include <iostream>

using namespace std;

//封装生日类
class Birthday
{
private:
int year;
int month;
int day;
public:
//无参构造函数
Birthday() { cout << "Birthday::无参构造函数" << endl;}
//有参构造函数
Birthday(int y, int m, int d):year(y),month(m),day(d)
{
cout << "Birthday::有参构造函数" << endl;
}
void show()
{
cout << "year= " << year << endl;
}
};


//封装学生类
class Stu
{
private:
string name;
int id;
Birthday bir;
public:
//无参构造函数
Stu() {cout << "Stu::无参构造函数" << endl;}
//有参构造函数
Stu(string name, int id, int year, int month, int day):name(name),id(id),bir(year,month,day)
//string name = name Birthday bir(year,month,day)
{
cout << "Stu::有参构造函数" << endl;
}
void show()
{
cout << "name = " << name << endl;
cout << "id = " << id << endl;
}
};
int main()
{
Stu s1("张三",1001,2002,2,8); //自动调用有参构造函数


Stu s2;//自动调用无参构造函数


return 0;
}

img

2 析构函数

2.1 功能

当类对象生命周期结束后,会自动调用析构函数,来给类对象回收资源(释放空间)。

2.2 格式

函数名:~类名

返回值:无返回值,也无void

参数:无参数

访问权限:一般为public

1
2
3
4
~类名()
{
函数体内容
}

2.3 调用时机

当类对象生命周期结束后,会自动调用析构函数。

1>栈区

当类对象所在的函数结束时,会自动调用析构函数释放空间。

2>堆区

何时使用delete,何时自动调用析构函数。

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
#include <iostream>

using namespace std;

//封装学生类
class Stu
{
private:
string name;
int id;
public:
Stu() {cout << "Stu::无参构造函数" << endl;}
//有参构造函数
Stu(string name, int id):name(name),id(id)
{
cout << "Stu::有参构造函数" << endl;
}
//析构函数
~Stu()
{
cout << "Stu::析构函数" << endl;
cout << this << endl;
}
void show()
{
cout << "name = " << name << endl;
}
};

int main()
{
Stu s1; //自动调用无参构造函数
Stu s2("张三",1001); //自动调用有参构造函数
cout << "&1= " << &s1 << " &s2= " << &s2 << endl;

return 0;
}

img

img

小结:

1> 类中都会有系统提供默认的析构函数,如果显性定义出析构函数,那么系统就取消默认提供。

2> 当类中有指针成员,并且指向堆区空间,那么此时就需要显性定义出析构函数,在析构函数中手动释放指针所指向的空间。如果使用系统提供的默认析构函数,则指针所指向的堆区空间就无法得到释放,从而造成内存泄漏。

3> 每个类中只有一个析构函数,原因:析构函数无参数,不能重载。

img

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
//封装学生类
class Stu
{
private:
string name;
int id;
double *score;
public:
Stu() {cout << "Stu::无参构造函数" << endl;}
//有参构造函数
Stu(string name, int id, double s):name(name),id(id),score(new double(s))
// double *score = new double(s)
{
cout << "Stu::有参构造函数" << endl;
}
//析构函数
~Stu()
{
cout << "Stu::析构函数" << endl;
delete score; //手动释放指针成员所申请的堆区空间
}
void show()
{
cout << "name = " << name << endl;
}
};


3 拷贝构造函数

3.1 功能

拷贝构造函数是一种特殊的构造函数,使用一个类对象给另一个类对象进行初始化使用的。

3.2 格式

函数名:与类同名

返回值:无返回值,也无void

参数:同类的其他类对象

访问权限:一般为public

1
2
3
4
类名(const 类名 &other)
{

}

3.3 调用时机

1> 用一个类对象给另一个类对象初始化时,自动调用拷贝构造函数

2> 当类对象作为函数的形参时,实参传递给形参的过程中,自定调用拷贝构造函数

3> 当函数返回一个类对象时,自动调用拷贝构造函数

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
#include <iostream>


using namespace std;


//封装学生类
class Stu
{
private:
string name;
int id;


public:
//无参构造函数
Stu() {cout << "Stu::无参构造函数" << endl;}
//有参构造函数
Stu(string name, int id):name(name),id(id)
{
cout << "Stu::有参构造函数" << endl;
}
//拷贝构造函数
//Stu(const Stu other) // Stu other = s2 --->other.Stu(s2)
Stu(const Stu &other):name(other.name),id(other.id)
{
//this->name = other.name; 赋值操作
cout << "Stu::拷贝构造函数" << endl;
}
//析构函数
~Stu()
{
cout << "Stu::析构函数" << endl;

}
void show()
{
cout << "name = " << name << endl;
cout << "id = " << id << endl;
}
};


int main()
{
Stu s1; // ===》 s1.Stu()


Stu s2("张三",1001); // s2.Stu("张三",1001)
s2.show();


Stu s3 = s2; // s3.Stu(s2)
s3.show();


return 0;
}

img

4 浅拷贝和深拷贝(笔试面试题)

1> 每个类中系统都会提供一个默认的拷贝构造函数,如果程序员显性定义出拷贝构造函数,则系统取消默认提供。

2> 系统提供的拷贝构造函数,是将一个类对象的所有数据成员给另一个对象的所有数据成员初始化的。该拷贝构造函数称为浅拷贝。

img

img

案例:

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
#include <iostream>


using namespace std;


//封装学生类
class Stu
{
private:
string name;
int id;
double *score;
public:
//无参构造函数
Stu()
{
cout <<"Stu::无参构造函数" << endl;
}
//有参构造函数
Stu(string name, int id, double score):name(name),id(id),score(new double(score))
//double *score = new double(score)
{
cout <<"Stu::有参构造函数" << endl;
}
// //拷贝构造函数 浅拷贝
// Stu(const Stu &other):name(other.name),id(other.id),score(other.score)
// // double *socre = other.score
// {
// cout <<"Stu::拷贝构造函数" << endl;
// }


//拷贝构造函数 深拷贝
Stu(const Stu &other):name(other.name),id(other.id),score(new double(*(other.score)))
// double *score = new double(*(other.score))
{
cout <<"Stu::拷贝构造函数" << endl;
}
//析构函数
~Stu()
{
cout <<"Stu::析构构造函数" << endl;
delete score;
}


void show()
{
cout << "name = " << name << endl;
}


};




int main()
{
Stu s1; // s1.Stu() 自动调用无参构造函数
Stu s2("zhangsan",1001,99); // s2.Stu("zhangsan",1001) 自动调用有参构造函数
Stu s3(s2); // s3.Stu(s2) 自动调用拷贝构造函数








return 0;
}


5 拷贝赋值函数

5.1 功能

用一个类对象给另一类对象进行赋值操作

本质:运算符重载

5.2 格式

函数名:operator=

返回值:自身的引用

参数:同类的类对象

访问权限:一般为public

1
2
3
4
类名 &operator=(const 类名&other)
{
函数体内容;
}

5.3 调用时机

用一个类对象给另一类对象进行赋值操作

案例

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
#include <iostream>


using namespace std;


//封装学生类
class Stu
{
private:
string name;
int age;
public:
//无参构造函数
Stu()
{
cout << "Stu::无参构造函数" << endl;
}
//有参构造函数
Stu(string name, int age):name(name),age(age)
{


cout << "Stu::有参构造函数" << endl;
}
//拷贝构造函数
Stu(const Stu &other):name(other.name),age(other.age)
{
cout << "Stu::拷贝构造函数" << endl;
}
//拷贝赋值函数
Stu &operator=(const Stu &other)
{
//避免自己给自己赋值
if(this != &other)
{
name = other.name;
age = other.age;
}


cout << "Stu:拷贝赋值函数" << endl;
return *this;
}


//析构函数
~Stu()
{
cout << "Stu::析构构函数" << endl;
}
void show()
{
cout << name << " " << age << endl;
}
};


int main()
{
Stu s1;
Stu s2("张三",18);
s2.show();


Stu s3 = s2; //===> s3.Stu(s2)
s3.show();


s1 = s3; //==》 s1.operator=(s3) 自动调用拷贝赋值函数
s1.show();

return 0;
}

img

1> 每个类中系统都会提供一个默认的拷贝赋值函数,功能是将一个类对象的所有数据成员赋值给另一个类对象的所有数据成员,如果程序员显性定义出拷贝赋值函数,那么系统取消默认提供。

2>拷贝赋值函数也存在深浅拷贝问题,系统提供的可以称为浅拷贝,深拷贝将拷贝赋值函数(拷贝构造函数)显性定义出来,将有指针成员,自己申请一片空间,再将另一个对象指针指向空间里的值,复制到刚刚申请的空间中去。

img

十三、匿名对象

1 概念:

没有名字的对象

2 格式:

类名();

3 作用

1> 用匿名对象给有名对象初始化

2> 用匿名对象给对象数组初始化

3> 用匿名对象最为函数的参数使用

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
#include <iostream>


using namespace std;


//封装狗类
class Dog
{
private:
string name;
string color;
int age;
public:
Dog()
{}
Dog(string n, string c, int a):name(n),color(c),age(a)
{}
Dog(const Dog &other):name(other.name),color(other.color),age(other.age)
{}
void show()
{
cout << name << " " << color << endl;
}
};


void fun(Dog d) // Dog d = Dog("小黑","black",7)
{

}


int main()
{
Dog d1 = Dog("大黄","pink",4);
d1.show();

Dog d[3] = {Dog("小黑","black",7),Dog("小","black",7)};



fun(Dog("小黑","black",7));


return 0;
}


十四、友元

1 作用

可以让一些函数或者一些类去访问另一个类的私有数据成员。

2 种类

全局函数做友元

类做友元

成员函数做友元

3 关键字

friend

4 全局函数做友元

让全局函数去访问一个类的私有属性。

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
#include <iostream>

using namespace std;

//封装房间类
class Room
{
//声明goodGay函数是本类的好朋友,就可以访问本类的所以属性
friend void goodGay(Room r);
public:
string living_room; //客厅
private:
string bed_room; //卧室
public:
Room()
{
living_room = "客厅";
bed_room = "卧室";
}
};

//全局函数
void goodGay(Room r)
{
cout << "全局函数好基友正在访问。。" << r.living_room << endl;
cout << "全局函数好基友正在访问。。" << r.bed_room << endl;
}

int main()
{
Room r;
goodGay(r);
return 0;
}

5 类作友元

让一个类去访问另一个类的私有属性。

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
#include <iostream>

using namespace std;

//声明有这样的类
class Room;

//封装好基友类
class GoodGay
{
private:
Room *r;
public:
GoodGay();
void vist();
};

//封装房间类
class Room
{
friend class GoodGay; //声明该类是本类的好朋友,可以访问本类的私有属性
public:
string livingRoom;
private:
string bedRoom;
public:
Room()
{
livingRoom = "客厅";
bedRoom = "卧室";
}
};

GoodGay::GoodGay()
{
r = new Room;
}
void GoodGay::vist()
{
cout << "好基友的类正在访问。。" << r->livingRoom << endl;


cout << "好基友的类正在访问。。" << r->bedRoom << endl;
}

int main()
{
GoodGay g;
g.vist();
return 0;
}

6 成员函数做友元(了解)

让一个类中的某个成员函数去访问另一个类的私有属性。

总结:

1> 不要过度的使用友元,会降低封装性,破坏封装性。

2> 友元不具有交互性、传递性、继承性。

十五、常成员函数和常对象(const)

类中所有的成员函数都可以对数据成员做修改操作,如果想让一个成员函数不能对数据成员最修改操作,此时就需要用常成员函数完成。

课前热身

1
2
3
4
5
6
7
8
9
10
int *p;
int * const p; //..
int const *p; //...
int const * const p;//...

const int fun() //表示该函数的返回值是一个常整型变量
{

}

1 常成员函数

常成员函数不能对数据成员做修改操作。

格式:

1
2
3
4
返回值类型 函数名(形参列表) const    //该成员函数是常成员函数
{
函数体内容;
}

注意:

一个类中,常成员函数与普通成员函数同名,不是重复定义,而是重载关系,主要原因:this指针类型不同。

案例:

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
#include <iostream>


using namespace std;


class Stu
{
private:
string name;
int age;
public:
Stu()
{}
Stu(string name,int age):name(name),age(age)
{}
void show() // Stu * const this
{
this->age = 48;
//this = nullptr;
cout << age << endl;
}
void show() const // Stu const * const this 常成员函数
{
//this->age = 88;
//this = nullptr;
//age = 58; 常成员函数不能对数据成员做修改操作


cout << this->age << endl;
}
};


int main()
{
Stu s1("张三",18);
s1.show();
return 0;
}


2 常对象

常对象表示该对象的数据成员不能被改变。

格式:

const 类名 对象名;

总结

1> 常对象,只能调用常成员函数,如果没有常成员函数,调动非常成员函数,则报错,原因是非常成员函数可以改变数据成员。

2> 非常对象,可以调用常成员函数,也可以调用非常成员函数,优先调用非常成员函数。

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
#include <iostream>

using namespace std;

class Stu
{
private:
string name;
int age;
public:
Stu()
{}
Stu(string name,int age):name(name),age(age)
{}

void init(string n, int a)
{
name = n;
age = a;
}

// void show() // Stu * const this 非常成员函数
// {
// this->age = 48;
// //this = nullptr;
// cout << age << endl;
// }
void show() const // Stu const * const this 常成员函数
{
//this->age = 88;
//this = nullptr;
//age = 58; 常成员函数不能对数据成员做修改操作


cout << this->age << endl;
}
};


int main()
{
const Stu s("zhang",78);
//s.init();

const Stu s1("张三",18); //常对象
s1.show();
return 0;
}

3 mutable关键字

mutable修饰成员变量,表示该成员变量可以在常成员函数中被修改(取消常属性)。

img

十五、运算符重载

1 概念

运算符重载就是对运算符进行重新定义,赋予另一种功能,以适应不同的数据类型。

每个运算符重载都有两种实现方式:

1> 成员函数实现

2> 全局函数实现

2 算术运算符重载

种类:+ 、 - 、 * 、 /、 %

表达式: L # R (L 左操作数 # 运算符 R右操作数)

左操作数:可以是左值,也可以是右值,运算过程中不能被改变。

右操作数:可以是左值,也可以是右值,运算过程中不能被改变。

结果:右值(不可以被改变)

1> 成员函数实现算术运算符重载:

1
2
3
4
5
6
7
8
9
const 类名 operator#(const 类名 &R) const

{}

//第一个const表示运算结果是个右值,不能被改变。

//第二个const表示右操作数在运算符过程中,不能被改变。

//第三个const表示左操作数在运算符过程中,不能被改变。

2> 全局函数实现算术运算符重载:

1
2
3
const 类名 operator#(const 类名 &L, const 类名 &R)

{}
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
#include <iostream>

using namespace std;

//封装 人 类
class Person
{
private:
int a;
int b;
public:
//friend const Person operator+(const Person &L, const Person &R);
Person() {}
Person(int a, int b):a(a),b(b)
{}

//成员函数实现+号运算符重载
const Person operator+(const Person &R) const
{
Person temp;
temp.a = a + R.a;
temp.b = b + R.b;
return temp;
}

void show()
{
cout << "a = " << a << " b = " << b << endl;
}
};

////全局函数实现+号运算符重载
//const Person operator+(const Person &L, const Person &R)
//{
// Person temp;
// temp.a = L.a + R.a;
// temp.b = L.b + R.b;
// return temp;
//}


int main()
{
Person p1(23,56);
Person p2(12,10);

// //简化版本
// Person p3 = p1 + p2; //成员函数版本的 本质: Person p3 = p1.operator+(p2)
// p3.show();

//简化版本
Person p3 = p1 + p2; //全局函数版本的 本质: Person p3 = operator+(p1,p2)
p3.show();



return 0;
}

3 关系运算符重载

种类: > 、 >= 、 < 、<= 、== 、 !=

表达式:L # R (L 左操作数 # 运算符 R右操作数)

左操作数:可以是左值,也可以是右值,运算过程中不能被改变。

右操作数:可以是左值,也可以是右值,运算过程中不能被改变。

结果:bool

1> 成员函数实现关系运算符重载:

1
2
3
bool operator#(const 类名 &R) const

{}

2> 全局函数实现关系运算符重载:

1
2
3
bool operator#(const 类名 &L, const 类名 &R)

{}
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
#include <iostream>

using namespace std;

//封装 人 类
class Person
{
private:
int a;
int b;
public:
//friend const Person operator+(const Person &L, const Person &R);
friend bool operator>(const Person &L, const Person &R);
Person() {}
Person(int a, int b):a(a),b(b)
{}

//成员函数实现+号运算符重载
const Person operator+(const Person &R) const
{
Person temp;
temp.a = a + R.a;
temp.b = b + R.b;
return temp;
}
// //成员函数实现>号运算符重载
// bool operator>(const Person &R) const
// {
// if(a > R.a && b > R.b)
// {
// return true;
// }
// else
// {
// return false;
// }
// }

void show()
{
cout << "a = " << a << " b = " << b << endl;
}
};

////全局函数实现+号运算符重载
//const Person operator+(const Person &L, const Person &R)
//{
// Person temp;
// temp.a = L.a + R.a;
// temp.b = L.b + R.b;
// return temp;
//}


//全局函数实现>号运算符重载
bool operator>(const Person &L, const Person &R)
{
if(L.a > R.a && L.b > R.b)
{
return true;
}
else
{
return false;
}
}

int main()
{
Person p1(23,56);
Person p2(12,10);

// //简化版本
// Person p3 = p1 + p2; //成员函数版本的 本质: Person p3 = p1.operator+(p2)
// p3.show();

//简化版本
Person p3 = p1 + p2; //全局函数版本的 本质: Person p3 = operator+(p1,p2)
p3.show();

// //简化版本
// if(p1 > p2) //成员函数版本的 本质: p1.operator>(p2)
// {
// cout << "p1 > p2" << endl;
// }
//简化版本
if(p1 > p2) //全局函数版本的 本质: operator>(p1,p2)
{
cout << "p1 > p2" << endl;
}
return 0;
}

4 赋值运算符重载

种类:= 、 += 、 -= 、*= 、/= 、%=

表达式: L # R (L左操作数 #运算符 R右操作数)

左操作数:只能是左值,运算过程中要发生变化。

右操作数:可以是左值,也可以是右值,运算过程中,不能发生变化。

结果:自身的引用

1> 成员函数实现赋值运算符重载:

1
2
3
类名 & operator#(const 类名 &R)

{}

2> 全局函数实现赋值运算符重载:

1
2
3
类名& operator#(类名 &L, const 类名 &R)

{}
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
#include <iostream>

using namespace std;

//封装 人 类
class Person
{
private:
int a;
int b;
public:
//friend const Person operator+(const Person &L, const Person &R);
friend bool operator>(const Person &L, const Person &R);
friend Person &operator+=(Person &L, const Person &R);
Person() {}
Person(int a, int b):a(a),b(b)
{}

//成员函数实现+号运算符重载
const Person operator+(const Person &R) const
{
Person temp;
temp.a = a + R.a;
temp.b = b + R.b;
return temp;
}
// //成员函数实现>号运算符重载
// bool operator>(const Person &R) const
// {
// if(a > R.a && b > R.b)
// {
// return true;
// }
// else
// {
// return false;
// }
// }


// //成员函数实现+=运算符重载
// Person &operator+=(const Person &R)
// {
// a += R.a; // a = a +R.a
// b += R.b;
// return *this;
// }


void show()
{
cout << "a = " << a << " b = " << b << endl;
}
};


////全局函数实现+号运算符重载
//const Person operator+(const Person &L, const Person &R)
//{
// Person temp;
// temp.a = L.a + R.a;
// temp.b = L.b + R.b;
// return temp;
//}



//全局函数实现>号运算符重载
bool operator>(const Person &L, const Person &R)
{
if(L.a > R.a && L.b > R.b)
{
return true;
}
else
{
return false;
}
}


//全局函数实现+=运算符重载
Person &operator+=(Person &L, const Person &R)
{
L.a += R.a;
L.b += R.b;
return L;


}
int main()
{
Person p1(23,56);
Person p2(12,10);


// //简化版本
// Person p3 = p1 + p2; //成员函数版本的 本质: Person p3 = p1.operator+(p2)
// p3.show();


//简化版本
Person p3 = p1 + p2; //全局函数版本的 本质: Person p3 = operator+(p1,p2)
p3.show();




// //简化版本
// if(p1 > p2) //成员函数版本的 本质: p1.operator>(p2)
// {
// cout << "p1 > p2" << endl;
// }
//简化版本
if(p1 > p2) //全局函数版本的 本质: operator>(p1,p2)
{
cout << "p1 > p2" << endl;
}


// //简化版本
// p1+=p2; //成员函数版本的 本质: p1.operator+=(p2)
// //p1 = p1 + p2 ----> p1.operator+=(p2)
// p1.show();


//简化版本
p1+=p2; //全局函数版本的 本质: operator+=(p1,p2)
//p1 = p1 + p2 ----> p1.operator+=(p2)
p1.show();


return 0;
}


5 自增、自减运算符重载

种类:++ 、 –

以自增为例:

1) 前置自增

表达式:++O (O操作数)

操作数:只能是左值,运算过程中需要被改变

结果:左值 (自身的引用)

1>成员函数实现前置自增运算符重载:

1
2
3
类名 &operator++()

{}

2>全局函数实现前置自增运算符重载:

1
2
3
类名 &operator++(类名 &O)

{}

2) 后置自增

表达式:O++

操作数:只能是左值,运算过程中需要被改变

结果:右值 (不能被改变)

1>成员函数实现后置自增运算符重载:

1
2
3
const 类名operator++(int)

{}

2>全局函数实现后置自增运算符重载:

1
2
3
const 类名operator++(类名 &O,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
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
#include <iostream>

using namespace std;

//封装 人 类
class Person
{
private:
int a;
int b;
public:
//friend const Person operator+(const Person &L, const Person &R);
friend bool operator>(const Person &L, const Person &R);
friend Person &operator+=(Person &L, const Person &R);
friend Person &operator++(Person &O);
friend const Person operator++(Person &O, int);
Person() {}
Person(int a, int b):a(a),b(b)
{}

//成员函数实现+号运算符重载
const Person operator+(const Person &R) const
{
Person temp;
temp.a = a + R.a;
temp.b = b + R.b;
return temp;
}
// //成员函数实现>号运算符重载
// bool operator>(const Person &R) const
// {
// if(a > R.a && b > R.b)
// {
// return true;
// }
// else
// {
// return false;
// }
// }


// //成员函数实现+=运算符重载
// Person &operator+=(const Person &R)
// {
// a += R.a; // a = a +R.a
// b += R.b;
// return *this;
// }


// //成员函数实现前++运算符重载
// Person &operator++()
// {
// ++a;
// ++b;
// return *this;
// }


// //成员函数实现后置++运算符重载
// const Person operator++(int)
// {
// Person temp;
// temp.a = a++;
// temp.b = b++;
// return temp;
// }


void show()
{
cout << "a = " << a << " b = " << b << endl;
}
};


////全局函数实现+号运算符重载
//const Person operator+(const Person &L, const Person &R)
//{
// Person temp;
// temp.a = L.a + R.a;
// temp.b = L.b + R.b;
// return temp;
//}




//全局函数实现>号运算符重载
bool operator>(const Person &L, const Person &R)
{
if(L.a > R.a && L.b > R.b)
{
return true;
}
else
{
return false;
}
}


//全局函数实现+=运算符重载
Person &operator+=(Person &L, const Person &R)
{
L.a += R.a;
L.b += R.b;
return L;


}


//全局函数实现前++运算符重载
Person &operator++(Person &O)
{
++O.a;
++O.b;
return O;
}


//全局函数实现后++运算符重载
const Person operator++(Person &O, int)
{
Person temp;
temp.a = O.a++;
temp.b = O.b++;
return temp;
}


int main()
{
Person p1(23,56);
Person p2(12,10);


// //简化版本
// Person p3 = p1 + p2; //成员函数版本的 本质: Person p3 = p1.operator+(p2)
// p3.show();


//简化版本
Person p3 = p1 + p2; //全局函数版本的 本质: Person p3 = operator+(p1,p2)
p3.show();




// //简化版本
// if(p1 > p2) //成员函数版本的 本质: p1.operator>(p2)
// {
// cout << "p1 > p2" << endl;
// }
//简化版本
if(p1 > p2) //全局函数版本的 本质: operator>(p1,p2)
{
cout << "p1 > p2" << endl;
}


// //简化版本
// p1+=p2; //成员函数版本的 本质: p1.operator+=(p2)
// //p1 = p1 + p2 ----> p1.operator+=(p2)
// p1.show();


//简化版本
p1+=p2; //全局函数版本的 本质: operator+=(p1,p2)
//p1 = p1 + p2 ----> p1.operator+=(p2)
p1.show();


cout << "=====================" << endl;
Person p4(10,10);




// //简化版本
// p1 = ++p4; //成员函数版本的 本质: p4.operator++()
// p4.show();
// p1.show();


//简化版本
p1 = ++p4; //全局函数版本的 本质: operator++(p4)
p4.show();
p1.show();


cout << "=====================" << endl;
Person p5(20,20);


// //简化版本
// p2 = p5++; //成员函数版本的 本质: p5.opeator++(int)
// p2.show();
// p5.show();


//简化版本
p2 = p5++; //全局函数版本的 本质: opeator++(p5,int)
p2.show();
p5.show();






return 0;
}


6 插入提取运算符重载

插入符 <<

提取符 >>

cout 是ostream类的类对象

cin 是istream类的类对象

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
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
#include <iostream>


using namespace std;


//封装 人 类
class Person
{
private:
int a;
int b;
public:
//friend const Person operator+(const Person &L, const Person &R);
friend bool operator>(const Person &L, const Person &R);
friend Person &operator+=(Person &L, const Person &R);
friend Person &operator++(Person &O);
friend const Person operator++(Person &O, int);
friend ostream &operator<<(ostream &cout, const Person &p);
friend istream & operator>>(istream &cin, Person &p);
Person() {}
Person(int a, int b):a(a),b(b)
{}


//成员函数实现+号运算符重载
const Person operator+(const Person &R) const
{
Person temp;
temp.a = a + R.a;
temp.b = b + R.b;
return temp;
}
// //成员函数实现>号运算符重载
// bool operator>(const Person &R) const
// {
// if(a > R.a && b > R.b)
// {
// return true;
// }
// else
// {
// return false;
// }
// }


// //成员函数实现+=运算符重载
// Person &operator+=(const Person &R)
// {
// a += R.a; // a = a +R.a
// b += R.b;
// return *this;
// }


// //成员函数实现前++运算符重载
// Person &operator++()
// {
// ++a;
// ++b;
// return *this;
// }


// //成员函数实现后置++运算符重载
// const Person operator++(int)
// {
// Person temp;
// temp.a = a++;
// temp.b = b++;
// return temp;
// }


void show()
{
cout << "a = " << a << " b = " << b << endl;
}
};


////全局函数实现+号运算符重载
//const Person operator+(const Person &L, const Person &R)
//{
// Person temp;
// temp.a = L.a + R.a;
// temp.b = L.b + R.b;
// return temp;
//}




//全局函数实现>号运算符重载
bool operator>(const Person &L, const Person &R)
{
if(L.a > R.a && L.b > R.b)
{
return true;
}
else
{
return false;
}
}


//全局函数实现+=运算符重载
Person &operator+=(Person &L, const Person &R)
{
L.a += R.a;
L.b += R.b;
return L;


}


//全局函数实现前++运算符重载
Person &operator++(Person &O)
{
++O.a;
++O.b;
return O;
}


//全局函数实现后++运算符重载
const Person operator++(Person &O, int)
{
Person temp;
temp.a = O.a++;
temp.b = O.b++;
return temp;
}


//全局函数实现<<运算符重载
ostream &operator<<(ostream &cout, const Person &p)
{
cout << p.a << endl;
cout << p.b << endl;
return cout;
}
//全局函数实现>>运算符重载
istream & operator>>(istream &cin, Person &p)
{
cin >> p.a >> p.b ;
return cin;
}


int main()
{
Person p1(23,56);
Person p2(12,10);


// //简化版本
// Person p3 = p1 + p2; //成员函数版本的 本质: Person p3 = p1.operator+(p2)
// p3.show();


//简化版本
Person p3 = p1 + p2; //全局函数版本的 本质: Person p3 = operator+(p1,p2)
p3.show();




// //简化版本
// if(p1 > p2) //成员函数版本的 本质: p1.operator>(p2)
// {
// cout << "p1 > p2" << endl;
// }
//简化版本
if(p1 > p2) //全局函数版本的 本质: operator>(p1,p2)
{
cout << "p1 > p2" << endl;
}


// //简化版本
// p1+=p2; //成员函数版本的 本质: p1.operator+=(p2)
// //p1 = p1 + p2 ----> p1.operator+=(p2)
// p1.show();


//简化版本
p1+=p2; //全局函数版本的 本质: operator+=(p1,p2)
//p1 = p1 + p2 ----> p1.operator+=(p2)
p1.show();


cout << "=====================" << endl;
Person p4(10,10);




// //简化版本
// p1 = ++p4; //成员函数版本的 本质: p4.operator++()
// p4.show();
// p1.show();


//简化版本
p1 = ++p4; //全局函数版本的 本质: operator++(p4)
p4.show();
p1.show();


cout << "=====================" << endl;
Person p5(20,20);


// //简化版本
// p2 = p5++; //成员函数版本的 本质: p5.opeator++(int)
// p2.show();
// p5.show();


//简化版本
p2 = p5++; //全局函数版本的 本质: opeator++(p5,int)
p2.show();
p5.show();


// //简化版本
// cout << p2 ; //成员函数版本的 本质: cout.operator<<(p2)


cout << "=====================" << endl;
//简化版本
cout << p2; //全局函数版本的 本质: operator<<(cout, p2)
// ostream & operator<<(ostream &cout, const Person &p)


Person p;






//简化版本
cin >> p; //成员函数版本的 本质:cin.operator>>(p)
//全局函数版本的 本质: operator>>(cin, p)
// istream & operator>>(istream &cin, Person &p)
cout << p;

return 0;
}


7 不能重载的运算符

对象访问成员 .

指针访问成员 ->

三目运算符 ? :

sizeof()

作用域限定符 ::

十六、静态成员

静态数据成员和静态成员函数是属于类的,不属于类的某个实例,它们在类的所有实例中都是共享的。

在数据成员前加 static ------>静态数据成员

在成员函数前加 static ------>静态成员函数

静态数据成员必须在类外初始化,如果不初始化(不建议),默认为0。

静态成员函数只能访问静态数据成员,不能访问非静态数据成员。

格式:

1
2
3
4
5
6
7
8
9
10
11
class 类名
{
static 数据类型 变量名; // 表示该变量时静态数据成员
static 返回值类型 函数名(形参列表) // 表示该变量时静态成员函数
{
}

};

数据类型 类名::变量名 = 初始化;

实例:银行账户

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
#include <iostream>


using namespace std;


//封装 银行账户 类
class BankAccount
{
private:
double balance; //余额
static double interestRate; //利率 静态数据成员




public:
//构造函数
BankAccount(double m):balance(m)
{}


//静态成员函数 获取当前的利率
static double getInterestRate()
{
//balance = 20; 不能访问非静态数据成员
//this->balance = 20 没有this指针


return interestRate; //可以访问静态数据成员
}


//静态成员函数 获取连本带利的余额
static double getLastMoney(const BankAccount &account)
{
return account.balance*interestRate+account.balance;
}


//静态成员函数 修改利率
static void update(double rate)
{
interestRate = rate;
}


void show()
{}


};


double BankAccount::interestRate = 0.05; //静态数据成员必须在类外初始化


int main()
{


cout << BankAccount::getInterestRate() << endl;


BankAccount::update(0.03);


cout << BankAccount::getInterestRate() << endl;


BankAccount zhangsan(1000);


cout << "连本带利的余额: " << BankAccount::getLastMoney(zhangsan) << endl;






return 0;
}


十七、继承

类的三大属性:封装、继承、多态

1 目的

1> 实现代码的复用性

2> 建立多个类(父类与子类)之间的联系

3> 通过继承,实现多态的特点

2 继承相关概念

保持已有类的特性,在原来的基础上增加新的特性而构造出新类的过程,称为继承/派生。

被继承者称为 父类 / 基类

继承者称为 子类 / 派生类

3 格式

1
2
3
4
5
6
7
class 类名:继承方式 类名
{
子类的拓展;
};

//继承方式 public 共有继承 protectd保护继承 private私有继承
//一般使用public继承

img

4 继承方式

1
2
3
4
5
6
7
8
父类中成员的访问权限                public | protected | private            public | protected | private                public | protected | private

继承方式 public protected private

子类通过该继承方式,继承到子类中 public | protected | 不可访问 protected | protected | 不可访问 private | 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
#include <iostream>


using namespace std;


//封装 人 类
class Person
{
private:
string name;
protected:
int age;
public:
int h;


Person()
{}
Person(string name, int age, int h):name(name),age(age),h(h)
{
cout << "父类的有参构造函数" << endl;
}
void show()
{
cout << name << endl;
cout << age << endl;
}
};


//封装 学生 类 共有继承于 人 类
class Stu:public Person
{
private:
double score;


public:
//无参构造
Stu()
{}
//有参构造函数
Stu(string name, int age, int h, double score):Person(name,age,h),score(score)
{
cout << "子类的有参构造函数" << endl;
}
void show()
{


cout << score << endl;
cout << h << endl; //在子类中可以访问从父类继承下来的共有成员
cout << age << endl; //在子类中可以访问从父类继承下来的保护成员
//cout << name << endl; //在子类中不可以访问从父类继承下来的私有成员
}
};


int main()
{
Stu s1("张三",18,185,88);
s1.show(); //默认调用子类的show


cout << "====================" << endl;


s1.Person::show();


return 0;
}


img

5 继承中的特殊成员函数

5.1 构造函数

基类中的构造函数会被继承到派生类中,先调用基类的构造函数来给派生类从基类中继承下来的数据成员初始化。(先构造父类,再构造子类)

5.2 析构函数

父类中的析构函数也会被继承到子类中,子类析构后,再调用父类的析构函数进行释放空间。(先构造的,后析构 后构造的,先析构)

5.3 拷贝构造函数

父类中的拷贝构造函数会被继承到子类中,调用父类中的拷贝构造函数来完成对子类从父类继承下来的数据成员初始化工作。

如果涉及到深拷贝,则需要在父类、子类中各自完成深拷贝工作。

5.4 拷贝赋值函数

父类中的拷贝赋值函数会被继承到子类中,调用父类中的拷贝赋值函数来完成对子类从父类继承下来的数据成员赋值工作。

如果涉及到深拷贝,则需要在父类、子类中各自完成深拷贝工作。

小结:

1> 父类的构造一定赶在子类之前,所以先调用父类的构造函数,再调用子类的构造函数。

2> 父类和子类有同名同类型的函数时,它们既不是重复定义,也不是重载。原因:作用域不同。

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
#include <iostream>


using namespace std;


//封装 人 类
class Person
{
private:
string name;
protected:
int age;
public:
int h;


//无参构造
Person()
{}
//有参构造
Person(string name, int age, int h):name(name),age(age),h(h)
{
cout << "父类的有参构造函数" << endl;
}
//拷贝构造函数
// 赋值兼容规则
//1.子类的对象初始化父类的对象
//2.子类的对象可以初始化基类的引用
//3.子类对象的地址可以赋值给父类的指针
Person(const Person &other):name(other.name),age(other.age),h(other.h)
{
cout << "Person::拷贝构造函数" << endl;
}


//拷贝赋值函数
Person &operator=(const Person &other)
{
if(this != &other)
{
name = other.name;
age = other.age;
h = other.h;
}
cout << "Person::拷贝赋值函数" << endl;
return *this;
}


//析构函数
~Person()
{
cout << "Person::析构函数" << endl;
}
void show()
{


cout << name << endl;
cout << age << endl;
}
};


//封装 学生 类 共有继承于 人 类
class Stu:public Person
{
private:
double score;


public:
//无参构造
Stu()
{}
//有参构造函数
Stu(string name, int age, int h, double score):Person(name,age,h),score(score)
{
cout << "子类的有参构造函数" << endl;
}
//拷贝构造函数
Stu(const Stu &other):Person(other), score(other.score)
{
cout << "Stu::拷贝构造函数" << endl;
}
//拷贝赋值函数
Stu &operator=(const Stu &other)
{
if(this != &other)
{
score = other.score;
Person::operator=(other); //表明是父类里面的operator=
}
cout << "Stu::拷贝赋值函数" << endl;
return *this;
}
//析构函数
~Stu()
{
cout << "Stu::析构函数" << endl;
}
void show()
{


cout << score << endl;
cout << h << endl; //在子类中可以访问从父类继承下来的共有成员
cout << age << endl; //在子类中可以访问从父类继承下来的保护成员
//cout << name << endl; //在子类中不可以访问从父类继承下来的私有成员
}
};


int main()
{
Stu s1("张三",18,185,88);
s1.show(); //默认调用子类的show


cout << "====================" << endl;


s1.Person::show();


cout << "====================" << endl;


Stu s2 = s1;


cout << "====================" << endl;


Stu s3;
s3 = s1;




return 0;
}


img

十八、多继承

1 概念

一个类由多个类共同派生。

2 格式

1
2
3
4
class 类名:继承方式1 类名1,继承方式2 类名2,继承方式3 类名3,...,继承方式n 类名n
{
子类的拓展;
};

3 实例

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
#include <iostream>


using namespace std;


//封装 沙发 类 -----> 父类
class Sofa
{
private:
string sitting;
public:
Sofa()
{}
//有参
Sofa(string s):sitting(s)
{
cout << "Sofa::有参构造函数" << endl;
}
void show()
{
cout << sitting << endl;
}
};


//封装 床 类 -----> 父类
class Bed
{
private:
string sleep;
public:
Bed()
{}
//有参
Bed(string s):sleep(s)
{
cout << "Bed::有参构造函数" << endl;
}
void show()
{
cout << sleep << endl;
}
};


//封装 沙发床 类 共有继承于沙发类 共有继承于床类 ----> 子类
class SofaBed:public Sofa,public Bed
{
private:
string color;
public:
SofaBed()
{}
//有参
SofaBed(string sit,string sleep, string c):Sofa(sit),Bed(sleep),color(c)
{
cout << "SofaBed::有参构造函数" << endl;
}
void show()
{
cout << color << endl;
}

};


int main()
{
SofaBed s("可坐","可躺","pink");
s.Sofa::show();
s.Bed::show();
return 0;
}


img

十九、菱形继承

1 概念

菱形继承又称为钻石继承,是由公共基类派生出多个中间子类,又由多个中间子类共同派生出汇聚子类。汇聚子类会得到多份中间子类从公共基类继承下来的数据成员,会造成空间浪费,没有必要。

问题:

1> 汇聚子类会得到多份中间子类从公共基类继承下来的数据成员,会造成空间浪费,没有必要。

2>会对公共基类的数据成员进行多次初始化,或多次释放。

解决问题:虚继承

2 格式

1
2
3
4
5
6
   A       --------> 公共基类
/ \
B C --------> 中间子类
\ /
D --------> 汇聚子类

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
#include <iostream>


using namespace std;


//封装 家具 类 -------> 公共基类
class JiaJU
{
private:
int w;
public:
JiaJU()
{}
JiaJU(int w):w(w)
{
cout << "JiaJu::有参构造函数" << endl;
}
~JiaJU()
{
cout << "JiaJu::析构函数" << endl;
}


};


//封装 沙发 类 -----> 中间子类
class Sofa:public JiaJU
{
private:
string sitting;
public:
Sofa()
{}
//有参
Sofa(string s,int w):JiaJU(w),sitting(s)
{
cout << "Sofa::有参构造函数" << endl;
}
~Sofa()
{
cout << "Sofa::析构函数" << endl;
}
void show()
{
cout << sitting << endl;
}
};


//封装 床 类 -----> 中间子类
class Bed:public JiaJU
{
private:
string sleep;
public:
Bed()
{}
//有参
Bed(string s,int w):JiaJU(w),sleep(s)
{
cout << "Bed::有参构造函数" << endl;
}
~Bed()
{
cout << "Bed::析构函数" << endl;
}
void show()
{
cout << sleep << endl;
}
};


//封装 沙发床 类 共有继承于沙发类 共有继承于床类 ----> 汇聚子类
class SofaBed:public Sofa,public Bed
{
private:
string color;
public:
SofaBed()
{}
//有参
SofaBed(string sit,string sleep, string c,int w):Sofa(sit,w),Bed(sleep,w),color(c)
{
cout << "SofaBed::有参构造函数" << endl;
}
~SofaBed()
{
cout << "SofaBed::析构函数" << endl;
}
void show()
{
cout << color << endl;
}


};




int main()
{
SofaBed s("可坐","可躺","pink",200);
// s.Sofa::show();
// s.Bed::show();
return 0;
}


img

二十、虚继承

1 作用

解决菱形继承存在的问题。

2 格式

在中间子类继承方式前加 virtual

1
2
3
4
class 类名:virtual 继承方式 类名
{
中间子类的拓展;
};

3 小结

虚继承会解决菱形继承存在的问题(具体阐述什么问题),由于只需要保留一份中间子类从公共基类继承下来的数据成员,程序不知该保留哪个中间子类的,所以就会直接调用公共基类的无参构造,导致公共基类的数据成员无法初始化,所以需要在汇聚子类中手动调用公共基类的有参构造函数,来完成公共基类的数据成员初始化操作。

二一、多态

类的三大属性:封装、继承、多态

静态多态 (编译时)-----> 函数重载

动态多态 (运行时)

多态:一种形式多种状态,就比如一个人有很多角色,有着不同的行为操作,取决于不同的情景。

img

1 概念

多态:父类的指针或者引用,可以指向或者初始化子类的对象,调用子类对父类重写的函数,进而展开子类的功能。

2 函数重写

1> 必须有继承关系

2> 子类和父类有同名同类型的函数

3> 父类中的该函数必须是虚函数

3 虚函数

1> 在函数前加 virtual ----> 虚函数

2> 虚函数满足继承,如果父类中的虚函数,被继承到子类中,那么在子类中该函数依然是虚函数,如果子类再被继承,那么在“孙类”中依然是虚函数。

实例:

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
#include <iostream>


using namespace std;


//封装 周 类
class Zhou
{


private:
string name;
int age;
public:
Zhou(){}
//有参


Zhou(string name, int age):name(name),age(age)
{}
virtual void speak() //虚函数
{
cout << "阿巴阿巴阿巴。。" << endl;
}
};


//封装 老师 类 继承于周类
class Teacher:public Zhou
{
private:
int id;
public:
Teacher(){}
Teacher(string name, int age, int id):Zhou(name,age),id(id)
{}
void speak()
{
cout << "同学们,今天学习多态,认真听讲。。" << endl;
}
};


//封装 游戏玩家 类 继承于周类
class Player:public Zhou
{
private:
string name;
public:
Player(){}
Player(string name, int age, string n):Zhou(name,age),name(n)
{}
void speak()
{
cout << "卧************ 一波一波。。" << endl;
}
};


int main()
{
Teacher t("周老师",18,1001);
Player g("又菜又爱玩",3,"刺激战场");




//定义一个父类的指针
Zhou *p;


p = &t; //父类的指针 指向子类的对象 老师的角色


p->speak(); //展开子类的功能 老师说的话


p = &g; // 游戏玩家角色


p->speak(); //游戏玩家说的话


return 0;
}


img

4 赋值兼容规则

父类的指针或者引用,可以指向或者初始化子类的对象

img

5 多态中,实现函数重写的原理

1> 类中有虚函数时,虚函数就会有一个虚指针

2> 虚指针在类的最前面,指向了虚函数表,虚函数表里记录了虚函数

3> 虚指针和虚函数表是实现多态的重要机制

img

img

6 虚析构函数

因为父类的指针指向子类对象,只作用于子类从父类继承下来的那片空间,如果释放父类指针,那么只会释放子类从父类继承下来的那片空间,而子类自己拓展的空间,并没有得到释放,造成内存泄漏。

作用:

1> 虚析构函数可以正确引导子类调用析构函数释放自己拓展空间

2> 在父类的析构函数前加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
#include <iostream>

using namespace std;

class Person
{
private:
string name;
public:
Person(){}
Person(string name):name(name)
{
}
virtual ~Person() //虚析构函数
{
cout <<"Person::析构函数" << endl;
}
};

class Stu:public Person
{
private:
int age;
public:
Stu(){}
Stu(string name, int age):Person(name),age(age)
{}
~Stu()
{
cout << "Stu::析构函数" << endl;
}
};


int main()
{
Person *p = new Stu("张三",18);
delete p;

return 0;
}

img

7 纯虚函数

当父类中的虚函数,只是用来被子类进行重写的,并且没有实现的意义,此时就可以将该虚函数设置成纯虚函数。

格式:

1
2
3
virtual 返回值类型 函数名(形参列表) = 0;  //纯虚函数

//在父类声明 在子类中实现

img

8 抽象类

概念:

抽象类不能具体实例化一个对象, 一般用来被继承的。

如果一个类中至少有一个纯虚函数,那么该类就是抽象类。

如果父类是抽象类,子类继承父类,但是没有对父类中的纯虚函数进行重写,那么子类也是抽象类,就不能实例化对象。

实例:

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
#include <iostream>

using namespace std;

class A //抽象类
{
private:
int a;
public:
virtual void show() = 0;//纯虚函数
};

class B:public A
{
public:
void show()
{}
};


int main()
{
//A a;
B b;
return 0;
}


试编程:

封装一个抽象类(动物类),再封装两个类,分别是狗,猫这样的类,通过子类对父类的中void speak(纯虚函数)重写,实现,父类指针调用不同子类的speak函数,实现不同的结果。

二二、模板

1> 模板就是建立一个通用模具,大大提高代码的复用性。

2> C++另一种编程思想: 泛型编程 主要利用技术 模板

3> C++提供了两个重要的模板机制: 函数模板类模板

生活中的例子:

img

img

1 模板的特点

1> 模板是通用的,不是万能的

2> 模板不可以直接使用

2 函数模板

函数模板就是建立一个通用的函数,其函数的参数类型不具体制定,用一个虚拟类型来代替。

2.1格式

1
2
template<typename T>  或  template<class T>
函数的定义

解释:

template --------> 表示声明开始创建模板

typename --------> 表示其后面的是一种数据类型 (typename 可以用class代替)

T ------> 表示虚拟类型(数据类型) 可以用其他字母替换

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
#include <iostream>

using namespace std;

//创建函数模板
template <typename T,typename N>
T add(T x, N y)
{
return x+y;
}


//T fun(T a) //需要再创建一个模板
//{

//}


//int add(int x, int y)
//{
// return x+y;
//}

//double add(double x, double y)
//{
// return x+y;
//}

//string add(string x, string y)
//{
// return x+y;
//}


int main()
{
cout << add(1,2) << endl;
cout << add(2.3,5.6) << endl;
cout << add('1','2') << endl;

add("---",9);

return 0;
}

3 类模板

类模板就是建立一个通用的类,其类中的数据成员类型不具体制定,用一个虚拟类型来代替。

3.1格式

1
2
template<typename T>
类的声明

解释:

template --------> 表示声明开始创建模板

typename --------> 表示其后面的是一种数据类型 (typename 可以用class代替)

T ------> 表示虚拟类型(数据类型) 可以用其他字母替换

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
#include <iostream>


using namespace std;


//创建类模板
template <typename T,typename N>
class B
{
private:
T name;
N age;
public:
B(){}
B(T name, N age):name(name),age(age)
{}
};




/*class Stu
{
private:
string name;
int age;
public:
Stu(){}
Stu(string name, int age):name(name),age(age)
{}

};


class A
{
private:
char a;
char b;
public:
A(){}
A(int a, int b):a(a),b(b)
{}
}*/


int main()
{
B<string, int> b1("张三",18); //使用类模板时,要求表明依赖模板参数类型

B<int, string> b2(100,"男");

return 0;
}

二三、auto

1 概念

C++11引入了自动类型推导,和Python不一样,C++中需要auto关键字引导。

2 作用

auto修饰变量时,可以自动推导出变量的数据类型

3 注意

1> auto修饰变量时,必须初始化

2> auto的右值,可以是右值,可以是表达式,也可以是函数的返回值

3> auto不能修饰函数的形参

4> auto不能修饰数组

5> auto不能修饰非静态数据成员

4 用途

img

1> auto 一般修饰数据类型表冗长的类型。

2> auto还可以修饰依赖模板参数的类型

3> auto可以修饰lambda表达式。

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
#include <iostream>


using namespace std;


int fun(int a, char b, double c, string d, int *e, char *f, double *g)
{
return 10;
}


template <typename T>
void fun2(T a)
{
//T b = a;
auto b = a;


cout << typeid (b).name() << endl;
}


int main()
{
int (*p)(int , char , double , string , int *, char *, double *) = fun;


auto p2 = fun;


fun2(1);


fun2('f');


return 0;
}


二四、lambda表达式 (C++11)

1 作用

当需要一个匿名的,临时的,可以捕获外界变量的函数时,可以用lambda表达式完成。

2 格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[](){}    //lambda表达式

[捕获外界变量的方式](形参列表)->return type{函数体内容}

[]:捕获外界变量的方式
1. [变量1,变量2] :表示进行值捕获,捕获的到变量和外界变量不是同一个变量,但是值相同,且不能
进行修改操作,如果想修改,则需要加上mutable关键字
2. [=] : 表示对外界的所有变量进行值捕获,捕获的到变量和外界变量不是同一个变量,但是值相同,且不能
进行修改操作,如果想修改,则需要加上mutable关键字
3. [&变量1,&变量2] :表示进行引用捕获,捕获的变量和外界变量时同一个变量,值相同,可以对其进行修改操作
4. [&] : 表示对外界的所有变量进行应用捕获,捕获的变量和外界变量时同一个变量,值相同,可以对其进行修改操作

():形参列表
-> :返回值类型

案例1:

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
#include <iostream>

using namespace std;

int main()
{
int a = 100, b = 200;


cout << "main: a = " << a << " &a = " << &a << endl;

//auto fun = [a,b]()mutable{ //对a,b进行值捕获
auto fun = [=]()mutable{ //对外界所有的变量进行值捕获
cout << "a = " << a << " &a = " << &a << endl;
//a = 200; 值捕获 不可以对值进行修改
a = 200;
cout << "a = " << a << " &a = " << &a << endl;
cout << b << endl;
};

fun();

cout << "main: a = " << a << " &a = " << &a << endl;

return 0;
}

案例2:

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
#include <iostream>


using namespace std;


int main()
{
int a = 100, b = 200;


cout << "main: a = " << a << " &a = " << &a << endl;


//auto fun = [a,b]()mutable{ //对a,b进行值捕获
//auto fun = [=]()mutable{ //对外界所有的变量进行值捕获
//auto fun = [&a,&b](){ //对a,b进行引用捕获
auto fun = [&](){ //对外界所有的变量进行引用捕获
cout << "a = " << a << " &a = " << &a << endl;


a = 200;
cout << "a = " << a << " &a = " << &a << endl;


cout << b << endl;


};


fun();


cout << "main: a = " << a << " &a = " << &a << endl;


return 0;
}


二五、C++中的数据类型转换

1 概念

C++中存在多种数据类型的转换方式,用于在不同的数据类型之间进行转换。

2 种类

以下是常见的数据类型转换方式:

1>隐式类型转换(自动类型转换)

​ 这是C++编译器自动执行的类型转换,通常在表达式中出现时发生。例如,将较小的整数转换为较大的整数类型,将整数提升为浮点数等。

1
2
3
int num_int = 10; 
double num_double = num_int; // 隐式将int转换为double

2>显示类型转换(强制类型转换)

​ 通过使用强制类型转换操作符来显示执行类型转换。这种转换可能会导致数据的截断或者精度丢失,因此要小心使用。

​ (1)静态转换(static_cast)

用于基本数据类型之间的转换

以及父类指针/引用转换为子类指针/引用

还可以用于不同类型的指针之间的转换

1
2
double num_double = 3.14; 
int num_int = static_cast<int>(num_double); // 显式将double转换为int

​ (2)动态转换(dynamic_cast)

通常用于多态类之间的指针或引用类型转换,确保类型安全。在运行时进行类型检查,只能用于具有虚函数的类之间的转换

1
2
3
4
5
6
7
class Base { 
virtual void foo() {}
};
class Derived : public Base {};

Base* base_ptr = new Derived;
Derived* derived_ptr = dynamic_cast<Derived*>(base_ptr); // 显式将基类指针转换为派生类指针

​ (3)常量转换(const_cast)

用于添加或移除指针或引用的常量性。它可以用来去除const限定符,但要注意潜在的未定义行为

1
2
3
const int a =10; //
int *p;
p = &a; // 合不合法? no
1
2
const int num_const = 5; 
int* num_ptr = const_cast<int*>(&num_const); // 去除const限定符

​ (4)重新解释转换(reinterpret_cast)

执行低级别的位模式转换,通常用于指针之间的类型转换。它可能导致未定义行为,因此要谨慎使用

1
2
int num = 42; 
float* float_ptr = reinterpret_cast<float*>(&num); // 重新解释转换

3> C风格类型转换

与C语言中的类型转换方式类似,包括以下几种:

    1. c样式转换:使用强制类型转换 操作符进行转换,类似与C语言中的类型转换
1
2
int num_int = 10; 
double num_double = (double)num_int; // C样式强制类型转换
    1. 函数样式转换(函数式转换):使用C++中的类型转换函数进行转换
1
2
int num_int = 10; 
double num_double = double(num_int); // C++函数样式类型转换

3 注意

需要注意的是,尽管C++提供了多种类型转换方式,但应该谨慎使用,以避免潜在的错误和问题。特别是在使用强制类型转换时,务必确保转换操作是安全的,以避免不必要的问题。

关键字总结

img

1> C++中一共有63个关键字,如上图所示,其中标红的为c语言中的关键字,有32个

  1. asm

:这是一个用于嵌入汇编语言代码的关键字。它允许你在C++代码中直接插入汇编指令,通常用于执行特定的底层操作。然而,由于现代C++提供了更强大的抽象和跨平台性,通常不建议使用这个关键字。

  1. explicit

:这个关键字通常用于禁止隐式类型转换的发生。当一个构造函数被声明为explicit时,它将不会在隐式类型转换中被调用,只能在显式构造函数调用中使用。

  1. export

:在C++中,export关键字用于指示一个模板的定义将在另一个文件中实例化。然而,在实际的C++标准中,export关键字的语法并未最终确认,并且在许多编译器中也未被实现。在C++20中,export被重新引入,但是它的主要用途是与模块化编程相关,而不是之前模板实例化的用法。

  1. goto

goto是一个跳转语句,允许你无条件地将程序的控制转移到指定的标签处。然而,由于使用goto会导致代码结构变得混乱和难以维护,现代编程实践通常建议避免使用它。

  1. register

:在早期的C语言标准中,register关键字用于建议编译器将变量存储在寄存器中,以便提高访问速度。然而,现代编译器已经能够智能地管理寄存器分配,所以使用register关键字通常不再有明显的性能提升,并且在C++17中已被弃用。

  1. volatile

volatile关键字用于告诉编译器不要对标记为volatile的变量进行优化,因为这些变量的值可能会在未知的时间被外部因素改变,比如硬件中断或多线程环境中的共享变量。这可以防止编译器对这些变量的读取和写入操作进行优化,以确保程序的行为是可预测的。

2> 数据类型相关的关键字

bool、true、false:对于bool类型数据的相关处理,值为true和false

char、wchar_t:char是单字符数据,wchar_t多字符数据

int、short、float、double、long:整数和实数的数据类型

signed、unsigned:定义有符号和无符号数据的说明符

auto:在c语言中,是存储类型,但是在C++中,是类型自动推导,注意事项有两个:

i> 连续定义多个变量时,初始值必须是相同数据类型,否则报错

ii> auto p=&m; 与auto* p = &m;规定是一样

explicit:防止数据隐式转换

typedef:类型重定义

sizeof:求数据类型的字节运算

3> 语句相关的关键字

switch、case、default:实现多分支选择结构

do、while、for:循环相关的关键字

break、continue、goto:跳转语句

if、else:选择结构

inline:内联函数

return:函数返回值

4> 存储类型相关的关键字

static、const、volatile、register、extern、auto

5> 构造数据类型相关

struct、union:结构体和共用体

enum:枚举

class:类

6> 访问权限:public、protected、private

7> 异常处理:throw、try、catch

8> 类4中相关使用关键字

this:指代自己的指针

friend:友元

virtual:虚

delete、default:对类的特殊成员函数的相关使用

例如:Test(const Test &) = default; ~Test() = delete;

mutable:取消常属性

using:引入数据,有三种使用方式

i> 使用命名空间的关键字

ii> 相当于类型重定义

iii> 修改子类中从父类继承下来成员的权限

operator:运算符重载关键字

9> 类型转换相关的关键字

static_cast、dynamic_cast、const_cast、reinterpret_cast

10> 模板相关的关键字:template、typename

11> 命名空间相关:using、namespace

12> export:导入相关模板类使用

13> 内存分配和回收:new、delete

二六、异常

上的了厅堂,下得了厨房。 写的了代码 , 查的出异常

可以优雅解决代码中的异常

try ------> 将可能发生异常的地方用try进行包裹

throw ------> 将发生异常的地方,使用throw进行抛出异常

catch ------> 使用catch接收异常

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
#include <iostream>

using namespace std;

int fun(int x, int y)
{
if(y!=0)
{
return x/y;
}
else
{
throw 'w';
}
}


int main()
{


try {


fun(2,0);


cout << "hello world!" << endl;


} catch (char e) {


if(e == 'w')
{
cout << "分母不能为0" << endl;
}
}

return 0;
}

二七、C++标准模板库 (STL)

C++中的标准模板库 (Standard Template Library),STL是标准库之一。

标准模板库中提供大量的函数模板和类模板,用来对数据结构的处理。

STL 的组成:容器、算法、迭代器

容器:置物之所也

数组、链表、二叉树、哈希、、

算法:问题之解法也

增、删、改、查

迭代器:是容器和算法之间的粘合剂 (== 指针)

使用vector需要包含头文件 #include

1 vector 容器

vector就是数组,也称为单端数组,和普通数组有区别,普通数组是静态空间,而vector的空间时动态拓展。

动态拓展,不是在原来空间后面续接新的空间,而是,重新申请空间,将原来空间里的数据,复制到刚刚申请的空间中,再释放原来的空间。

2 vector构造函数

函数原型:

vector< T > v; //无参构造

vector(const vectcor &v) ; //拷贝构造函数

vector(n,elem); //将n个elem拷贝给对象本身

vector(v.begin(),v.end()); //将区间[v.begin(),v.end())拷贝给对象本身

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
#include <iostream>
#include <vector> //包含头文件

using namespace std;


void printVector(vector<int> &v)
{
//遍历容器中的每个元素
//迭代器
vector<int>::iterator iter; // == 指针
for(iter = v.begin(); iter != v.end(); iter++)
{
cout << *iter << " ";
}
cout << endl;
}


int main()
{
vector<int> v; //容器


v.push_back(10); //尾插
v.push_back(20);
v.push_back(30);
v.push_back(40);
v.push_back(50);

printVector(v);

vector<int> v2 = v; //拷贝构造函数

printVector(v2);

vector<int> v3(6,6);

printVector(v3);

vector<int> v4(v3.begin(), v3.end());

printVector(v4);


return 0;
}

3 vector赋值函数

函数原型:

vector &operator=(const vector &v); //拷贝赋值函数

assgin(v.begin(),v.end()); //将区间[v.begin(),v.end())赋值给对象本身

assgin(n,elem); //将n个elem赋值给对象本身

1
2
3
4
5
6
7
8
9
10
11
12
13
vector<int> v5;

v5 = v2; //拷贝赋值

printVector(v5);

v5.assign(v3.begin(),v3.end());

printVector(v5);

v5.assign(8,99);

printVector(v5);

4 vector的容量大小

empty() ; //判读容器是否为空

size(); //容器的大小,元素的个数

capacity(); // 容量的大小

resize(); //重新设置大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if(!v5.empty())
{
cout << v5.size() << endl;
cout << v5.capacity() << endl;
}


v5.resize(10); //多的部分用0补充

printVector(v5);

v3.resize(3); //少的部分 直接删除
printVector(v3);


5 vector的插入和删除

函数原型:

push_back(); //尾插

pop_back(); //尾删

insert(iterator_pos, elem); //在迭代器所指向的位置插入数据

insert(iterator_pos, n,elem);//在迭代器所指向的位置插入n个数据

eraser(v.begin(),v.end()); //将区间[v.begin(),v.end())的元素删除

clear(); //清空

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
v3 = v;
v3.pop_back();
printVector(v3);


v3.insert(v3.begin()+1,99);
printVector(v3);


v3.insert(v3.begin()+3,5,100);
printVector(v3);


v3.erase(v3.begin(),v3.end());
printVector(v3);

6 vector的元素访问

函数原型:

opeator[](int idx);

at(int idx);

front(); //第一个元素

back(); //最后一个元素

二八、list

1概念

功能:

将数据进行链式存储

链表(list) 是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的。

链表的组成:

链表由一系列节点组成

节点的组成:

一个是存储数据元素的数据域,另一个是存储下一个节点地址的指针域

STL中的链表是一个双向链循环链表

list的优点:

  • 采用动态存储分配,不会造成内存浪费和溢出

  • 链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素

list缺点:

  • 链表灵活,但是空间(指针域)和 时间(遍历)额外消耗比较大

2 list构造函数

函数原型:

1
2
3
4
5
6
7
- list<T> lst;	//无参构造函数

- list(beg, end); //构造函数将[begin,end)区间中对的元素拷贝给本身

- list(n, elem); //构造函数将n个elem拷贝给本身

- list(const list<T>& l); //拷贝构造函数

3 list赋值和交换

函数原型:

1
2
3
4
5
6
7
- assign(beg, end);  //将[beg,end)区间中的数据拷贝赋值给本身

- assign(n, elem); //将n个elem拷贝赋值给本身

- list& operator=(const list &lst); //重载赋值符

- swap(lst); //将lst与本身的元素互换

6.4 list大小操作

函数原型:

1
2
3
4
5
6
7
8
9
10
- size();	//返回容器中元素的个数

- empty(); //判断容器是否为空

- resize(num); //重新指定容器的长度为um,若容器变长,则以默认值填充新空间

- //如果容器变短,则末尾超出容器长度的元素则被删除

- resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新空间
//如果容器变短,则末尾超出容器的元素被删除。

6.5 list插入和删除

函数原型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- push_back();			//尾部插入元素

- pop_back(); //删除最后一个元素

- push_front(elem); //在容器的开头插入一个元素

- pop_front(); //在容器的开头删除一个元素

- insert(const_iterator pos, ele); //迭代器指向位置pos插入元素ele

- insert(const_iterator pos, int count, ele); //迭代器指向位置pos 插入count个元素ele

- insert(pos,beg, end); //在pos位置插入[beg,end)区间的数据,无返回值。

- erase(const_iterator pos); //删除迭代器指向的元素

- erase(const_iterator start, const_iterator end); //删除迭代器从start到end之间的元素

- clear(); //删除容器中所有的元素

- remove(elem); //删除容器中所有与elem值匹配的元素。

6.6 list 数据存取

函数原型:

1
2
3
- front();    //返回第一个元素

- back(); //返回最后一个元素

二九、智能指针