0、c++期末总结

重点已经用黑体和高亮表示,属于记忆理解部分,可收藏使用

0.1、程序的构成

  1. 一个C++程序可以由一个程序单位或多个程序单位构成。每一个程序单位作为一个文件。在程序编译时,编译系统分别对各个文件进行编译,因此,一个文件是一个编译单元。

0.2、程序的编写与实践

  1. 用高级语言编写的程序称为“源程序”,C++的源程序是以.cpp作为后缀的
  2. 对源程序(.cpp)进行编译 ➡ 目标程序(.obj) ➡ 二进制文件(.exe)
  3. 编写C++程序一般需要经过的几个步骤是:编辑➡编译➡连接➡调试

1、数据类型

1.1、整型

作用:整型变量表示的是整数类型的数据

  • int - 占4个字节
  • unsigned int - 无符号整型,占4个字节

1.2、sizeof关键字

作用:利用sizeof关键字可以统计数据类型所占内存大小

1.3、浮点型

作用:用于表示小数

浮点型变量分为两种:

  1. 单精度float ➡占4个字节,提供6位有效数组
  2. 双精度double ➡占8个字节,提供15位有效数字

1.4、数值型常量

1.4.1数值常量

一个整型常量可以用3种不同的方式表示:

  1. 十进制整数。如1357,-432,0等
  2. 八进制整数。在常数的开头加一个数字0,就表示这是以八进制数形式表示的常数。
  3. 十六进制整数。在常数的开头加一个数字0和一个英文字母X(或x),就表示这是以十六进制数形式表示的常数

1.4.2浮点数的表示方法

一个浮点数可以用两种不同的方式表示:

  1. 十进制小数形式。如21.456,-7.98等。
  2. 指数形式(即浮点形式)

1.5、字符型常量

作用:字符型变量用于显示单个字符

语法:char ch = 'a';

注意1:在显示字符型变量时,用单引号将字符括起来,不要用双引号

注意2:单引号内只能有一个字符,不可以是字符串

  • C和C++中字符型变量只占用1个字节。

  • 字符常量只能包括一个字符,如′AB′ 是不合法的

  • 字符常量区分大小写字母,如′A′′a′是两个不同的字符常量

  • 字符型变量并不是把字符本身放到内存中存储,而是将对应的ASCII编码放入到存储单元

1.6、字符串型常量

作用:用于表示一串字符

两种风格

  1. C风格字符串char 变量名[] = "字符串值"
  1. char str1[] = "hello world";

注意:C风格的字符串要用双引号括起来

  1. C++风格字符串string 变量名 = "字符串值"
  1. string str = "hello world";

注意:C++风格字符串,需要加入头文件==#include==

字符串常量为双引号


常考:字符串 "abc" 在内存中占几个字节?

答:占4个字节,而不是3个字节,编译系统会在字符串最后自动加一个′\0′作为字符串结束标志。但′\0′并不是字符串的一部分,它只作为字符串的结束标志


常考: 字符串常量″abc\n″包含几个字符?

答:不是5个而是4个字符,其中“\n”是一个转义字符,但它在内存中占5个字节

1.7、布尔类型bool

作用:布尔数据类型代表真或假的值

bool类型只有两个值:

  • true — 真(本质是1)
  • false — 假(本质是0)

bool类型占1个字节大小

1.8、数据的输入输出

关键字:cin、cout

语法: cin >> 变量1>>变量2>>....>>变量ncout<<表达式1<<表达式2<<...<<表达式n

  1. cout<<a,b,c; //错误,不能一次插入多项
  2. cout<<a+b+c; //正确,这是一个表达式,作为一项
  3. cin>>a>>b>>c>>d;

1.9、变量命名规则

  • C++规定标识符只能由字母、数字和下划线3种字符组成,且第一个字符必须为字母或下划线
  • 不能是关键字
  • 区分大小写

1.10、局部变量

  • 在一个函数内部定义的变量是局部变量,它只在本函数范围内有效,也就是说只有在本函数内才能使用它们,在此函数以外是不能使用这些变量的
  • 形参也是局部变量

1.11、全局变量

  • 在函数之外定义的变量是外部变量,称为全局变量

2、运算符

2.1、算数运算符

运算符 术语 示例 结果
+ 正号 +3 3
- 负号 -3 -3
+ 10 + 5 15
- 10 - 5 5
* 10 * 5 50
/ 10 / 5 2
% 取模(取余) 10 % 3 1
++ 前置递增 a=2; b=++a; a=3; b=3;
++ 后置递增 a=2; b=a++; a=3; b=2;
前置递减 a=2; b=–a; a=1; b=1;
后置递减 a=2; b=a–; a=1; b=2;

注意

  • 两个整数相除结果依然是整数(这里不进行四舍五入,直接舍去小数点后面数字)
  • C++中两个小数可以相除
  • 运算的两个数中有一个数为float型数据,则运算的结果是double型,因为C++在运算时对所有float型数据都按double型数据处理
  • 只有整型变量可以进行取模运算,两个小数不可以取模
  • 在除法运算中,除数不能为0
  • 取模运算时,除数也不能为0

常考:

  • 前置后置运算符单独使用没有什么区别

  • 前置递增先对变量进行++,再计算表达式

  • 后置递增先计算表达式,后对变量进行++

  • 请详细看下方代码并理解

  1. //递增
  2. int main() {
  3. //后置递增
  4. int a = 10;
  5. a++; //等价于a = a + 1
  6. cout << a << endl; // 11
  7. //前置递增
  8. int b = 10;
  9. ++b;
  10. cout << b << endl; // 11
  11. //区别
  12. //前置递增先对变量进行++,再计算表达式
  13. int a2 = 10;
  14. int b2 = ++a2 * 10;
  15. cout << b2 << endl; //110
  16. //后置递增先计算表达式,后对变量进行++
  17. int a3 = 10;
  18. int b3 = a3++ * 10;
  19. cout << b3 << endl; //100
  20. system("pause");
  21. return 0;
  22. }

2.2、赋值运算符

作用:用于将表达式的值赋给变量

  • 请详细看表格即可
运算符 术语 示例 结果
\= 赋值 a=2; b=3; a=2; b=3;
+= 加等于 a=0; a+=2; a=2;
-= 减等于 a=5; a-=3; a=2;
*= 乘等于 a=2; a*=2; a=4;
/= 除等于 a=4; a/=2; a=2;
%= 模等于 a=3; a%2; a=1;

2.3、比较运算符

作用:用于表达式的比较,并返回一个真值或假值

  • 请详细看表格即可
运算符 术语 示例 结果
\== 相等于 4 == 3 0
!= 不等于 4 != 3 1
< 小于 4 < 3 0
> 大于 4 > 3 1
<= 小于等于 4 <= 3 0
>= 大于等于 4 >= 1 1

注意:C和C++ 语言的比较运算中, “真”用数字“1”来表示, “假”用数字“0”来表示。

期末复习CPP - 图1

2.4、逻辑运算符

作用:用于根据表达式的值返回真值或假值

  • 请详细看表格即可
运算符 术语 示例 结果
! !a 如果a为假,则!a为真; 如果a为真,则!a为假。
&& a && b 如果a和b都为真,则结果为真,否则为假。
¦¦ a ¦¦ b 如果a和b有一个为真,则结果为真,二者都为假时,结果为假。
  • !非为三者中运算符最高的

期末复习CPP - 图2

3、流程结构

3.1、选择结构

3.1.1、if语句

  • if语句较为简单,常常搭配else使用,且可以嵌套使用

3.1.2、三目运算符

作用: 通过三目运算符实现简单的判断

语法:表达式1 ? 表达式2 :表达式3

如果表达式1的值为真,执行表达式2,并返回表达式2的结果;

如果表达式1的值为假,执行表达式3,并返回表达式3的结果。

  • 此处常出程序阅读题!!!!

3.1.3、switch语句

作用:执行多条件分支语句

语法

  1. switch(表达式)
  2. {
  3. case 结果1:执行语句;break;
  4. case 结果2:执行语句;break;
  5. ...
  6. default:执行语句;break;
  7. }
  • switch语句中表达式类型只能是整型或者字符型
  • case里如果没有break,那么程序会一直向下执行

3.2、循环结构

3.2.1、while

作用:满足循环条件,执行循环语句

语法: while(循环条件){ 循环语句 }

解释:只要循环条件的结果为真,就执行循环语句

3.2.2、do…while

作用: 满足循环条件,执行循环语句

语法: do{ 循环语句 } while(循环条件);

注意:与while的区别在于do…while会先执行一次循环语句,再判断循环条件(这里常考两者区别,记住无论怎样,do…while都会必然执行一次循环语句)

3.2.3、for

  • for循环中的表达式,要用分号进行分隔
  1. int main() {
  2. for (int i = 0; i < 10; i++)
  3. {
  4. cout << i << endl;
  5. }
  6. system("pause");
  7. return 0;
  8. }

3.3、跳转语句

3.3.1、break

作用: 用于跳出选择结构或者循环结构

3.3.2、continue

作用:在循环语句中,跳过本次循环中余下尚未执行的语句,继续执行下一次循环

注意:continue并没有使整个循环终止,而break会跳出循环

4、数组

数组:所谓数组,就是一个集合,存放相同类型的数据元素

  • 数组中的每个数据元素都是相同的数据类型
  • 数组是由连续的内存位置组成的

4.1、一维数组

一维数组定义的三种方式:

  1. 数据类型 数组名[ 数组长度 ];(常用,了解其余两种即可)
  2. 数据类型 数组名[ 数组长度 ] = { 值1,值2 ...};
  3. 数据类型 数组名[ ] = { 值1,值2 ...};
  1. int score[10];
  2. int score2[10] = { 100, 90,80,70,60,50,40,30,20,10 };
  3. int score3[] = { 100,90,80,70,60,50,40,30,20,10 };
  4. 123
  • 数组中下标是从0开始索引
  • 在对全部数组元素赋初值时,可以不指定数组长度
  • 直接打印数组名,可以查看数组所占内存的首地址
  • 对数组名进行sizeof,可以获取整个数组占内存空间的大小
  • 以上三种方式并不要求都会,但是需要都见过,防止在程序改错中乱改

4.1.1、一维数组初始化

  1. 在定义数组时分别对数组元素赋予初值。例如
  1. int a[10]={0123456789};
  2. 1
  1. 可以只给一部分元素赋值。例如
  1. int a[10]={0,1,2,3,4};
  2. 1
  1. 如果想使一个数组中全部元素值为1,可以写成
  1. int a[10]={1111111111};
  2. int a[10]={1*10}; // 错误写法,不能给数组整体赋初值
  3. 12
  1. 在对全部数组元素赋初值时,可以不指定数组长度
  1. int a[5]={12345};
  2. // 可以写成
  3. int a[]={12345};
  4. 123

4.2、二维数组

二维数组定义的四种方式:

  1. 数据类型 数组名[ 行数 ][ 列数 ];
  2. 数据类型 数组名[ 行数 ][ 列数 ] = { {数据1,数据2 } ,{数据3,数据4 } };
  3. 数据类型 数组名[ 行数 ][ 列数 ] = { 数据1,数据2,数据3,数据4};
  4. 数据类型 数组名[ ][ 列数 ] = { 数据1,数据2,数据3,数据4};
  1. int arr[2][3];
  2. int arr2[2][3] =
  3. {
  4. {1,2,3},
  5. {4,5,6}
  6. };
  7. int arr3[2][3] = { 1,2,3,4,5,6 };
  8. int arr4[][3] = { 1,2,3,4,5,6 };
  9. 1234567891011
  • 以上4种定义方式,利用第二种更加直观,提高代码的可读性
  • 如果对全部元素赋初值,定义数组时对第一维的长度可以不指定,但是第二维的长度不能省略

4.3、字符数组

  • 用来存放字符数据的数组是字符数组,字符数组中的一个元素存放一个字符

定义

  1. char c[10] = {′I′,′ ′,′a′,′m′,′ ′,′h′,′a′,′p′,′p′,′y′};
  2. 1

赋值

只能对字符数组的元素赋值,而不能用赋值语句对整个数组赋值

  1. char c[5];
  2. c={′C′,′h′,′i′,′n′,′a′}; //错误,不能对整个数组一次赋值
  3. C[0]=′C′; c[1]=′h′; c[2]=′i′; c[3]=′n′; c[4]=′a′; //对数组元素赋值,正确
  4. int a[5],b[5]={1,2,3,4,5};
  5. a=b; //错误,不能对整个数组整体赋值
  6. a[0]=b[0]; //正确,引用数组元素
  7. 1234567

4.4、字符串函数

  • 字符串连接函数 strcat

  • 字符串复制函数 strcpy

  • 字符串比较函数 strcmp

  • 字符串长度函数 strlen

5、函数

作用:将一段经常使用的代码封装起来,减少重复代码

5.1、函数定义

函数的定义一般主要有5个步骤:

1、返回值类型

2、函数名

3、参数表列

4、函数体语句

5、return 表达式

语法:

  1. 返回值类型 函数名 (参数列表)
  2. {
  3. 函数体语句
  4. return表达式
  5. }
  • 返回值类型 :一个函数可以返回一个值。在函数定义中
  • 函数名:给函数起个名称
  • 参数列表:使用该函数时,传入的数据
  • 函数体语句:花括号内的代码,函数内需要执行的语句
  • return表达式: 和返回值类型挂钩,函数执行完后,返回相应的数据

示例:定义一个加法函数,实现两个数相加

  1. //函数定义
  2. int add(int num1, int num2)
  3. {
  4. int sum = num1 + num2;
  5. return sum;
  6. }

5.2、函数调用

功能:使用定义好的函数

语法: 函数名(参数)

  1. int result = add(10,20);
  2. 1
  • 函数定义里小括号内称为形参,函数调用时传入的参数称为实参

  • 例如此处的num1,num2 为形参,10,20为实参

  • 函数不能嵌套定义但是可以嵌套调用(常考)

5.3、函数声明

  • 函数的声明可以多次,但是函数的定义只能有一次
  1. //声明可以多次,定义只能一次
  2. //声明
  3. int max(int a, int b);
  4. int max(int a, int b);
  5. //定义
  6. int max(int a, int b)
  7. {
  8. return a > b ? a : b;
  9. }
  10. int main() {
  11. int a = 100;
  12. int b = 200;
  13. cout << max(a, b) << endl;
  14. system("pause");
  15. return 0;
  16. }

5.4、值传递

  • 所谓值传递,即单向传递,就是函数调用时实参将数值传入给形参,而不能由形参传回来给实参。
  • 值传递时,如果形参发生改变,并不会影响实参(值传递时,形参是修饰不了实参的),请务必理解并记住,此处因篇幅就不进行讲解了!

5.5、函数默认参数

在C++中,函数的形参列表中的形参是可以有默认值的。

语法: 返回值类型 函数名 (参数= 默认值){}

  1. int func(int a, int b = 10, int c = 10) {
  2. return a + b + c;
  3. }
  4. //1. 如果某个位置参数有默认值,那么从这个位置往后,从左向右,必须都要有默认值
  5. //2. 如果函数声明有默认值,函数实现的时候就不能有默认参数
  6. int func2(int a = 10, int b = 10);
  7. int func2(int a, int b) {
  8. return a + b;
  9. }
  10. int main() {
  11. cout << "ret = " << func(20, 20) << endl;
  12. cout << "ret = " << func(100) << endl;
  13. system("pause");
  14. return 0;
  15. }
  • 如果某个位置参数有默认值,那么从这个位置往后,从左向右,必须都要有默认值
  • 如果函数声明有默认值,函数实现的时候就不能有默认参数

5.6、函数占位参数

C++中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置

语法: 返回值类型 函数名 (数据类型){}

  1. //函数占位参数 ,占位参数也可以有默认参数
  2. void func(int a, int) {
  3. cout << "this is func" << endl;
  4. }
  5. int main() {
  6. func(10,10); //占位参数必须填补
  7. system("pause");
  8. return 0;
  9. }

5.7、函数重载

作用:函数名可以相同,提高复用性

函数重载满足条件:

  • 函数名称相同
  • 函数参数类型不同 或者 个数不同 或者 顺序不同

注意: 函数的返回值不可以作为函数重载的条件

  1. //函数重载需要函数都在同一个作用域下
  2. void func()
  3. {
  4. cout << "func 的调用!" << endl;
  5. }
  6. void func(int a)
  7. {
  8. cout << "func (int a) 的调用!" << endl;
  9. }
  10. void func(double a)
  11. {
  12. cout << "func (double a)的调用!" << endl;
  13. }
  14. void func(int a ,double b)
  15. {
  16. cout << "func (int a ,double b) 的调用!" << endl;
  17. }
  18. void func(double a ,int b)
  19. {
  20. cout << "func (double a ,int b)的调用!" << endl;
  21. }
  22. //函数返回值不可以作为函数重载条件
  23. //int func(double a, int b)
  24. //{
  25. // cout << "func (double a ,int b)的调用!" << endl;
  26. //}
  27. int main() {
  28. func();
  29. func(10);
  30. func(3.14);
  31. func(10,3.14);
  32. func(3.14 , 10);
  33. system("pause");
  34. return 0;
  35. }

5.8、重载运算符规则

C++中有以下五个运算符不能重载

成员访问运算符 成员指针访问运算符 域运算符 长度运算符 条件运算符
. .* :: sizeof ?:

重载运算符规则

  1. 重载不能改变运算符运算对象(即操作数)的个数
  2. 重载不能改变运算符的优先级别
  3. 重载不能改变运算符的结合性
  4. 运算符重载函数可以是类的成员函数,也可以是类的友元函数,还可以是既非类的成员函数也不是友元函数的普通函数

什么时候应该用成员函数方式,什么时候应该用友元函数方式?二者有何区别呢?()

  • 一般将单目运算符重载为成员函数,将双目运算符(二元运算符)重载为友元函数
    • 重载为类的成员函数 - operator 函数有一个参数
    • 重载为类的友元函数 - operator 函数有两个参数
    • 只能将重载 “>>” (流插入运算符)和“<<” (流提取运算符)的函数作为友元函数或者普通函数重载,而不能将它们定义为成员函数,因为参数为两个
    • 类型转换运算符只能作为成员函数重载

单目运算符:只有一个操作数,如 !,-(负号),&,*,++,–

双目运算符:*,/,%,+,-,==,!=,<,>,<=,>=,&&,||

5.9、内联函数

  • 指定内置函数的方法很简单,只需在函数首行的左端加一个关键字inline即可。
  1. inline int max(int a,int b);
  2. 1
  • 使用内置函数可以节省运行时间
  • 只有那些规模较小而又被频繁调用的简单函数,才适合于声明为inline函数。

5.10、函数模板

语法:

  1. template<typename T>
  2. 函数声明或定义
  3. 12

解释:

template — 声明创建模板

typename — 表面其后面的符号是一种数据类型,可以用class代替

T — 通用的数据类型,名称可以替换,通常为大写字母

只适用于函数体相同、函数的参数个数相同而类型不同的情况,如果参数的个数不同,则不能用函数模板。

6、指针

指针的作用: 可以通过指针间接访问内存

6.1、指针变量

指针变量定义语法: 数据类型 * 变量名;

  • 请看下方代码示例,理解指针变量的定义与使用,期末一般不会出太难指针的题,但是基本用法一定要会!!
  1. int main() {
  2. //1、指针的定义
  3. int a = 10; //定义整型变量a
  4. //指针定义语法: 数据类型 * 变量名 ;
  5. int * p;
  6. //指针变量赋值
  7. p = &a; //指针指向变量a的地址
  8. cout << &a << endl; //打印数据a的地址
  9. cout << p << endl; //打印指针变量p
  10. //0073F8BC
  11. //0073F8BC
  12. //2、指针的使用
  13. //通过*操作指针变量指向的内存
  14. cout << "*p = " << *p << endl;
  15. // *p = 10
  16. system("pause");
  17. return 0;
  18. }

指针变量和普通变量的区别

  • 普通变量存放的是数据,指针变量存放的是地址
  • 指针变量可以通过” * “操作符,操作指针变量指向的内存空间,这个过程称为解引用

总结1: 我们可以通过 & 符号 获取变量的地址

总结2:利用指针可以记录地址

总结3:对指针变量解引用,可以操作指针指向的内存

总结4:所有指针类型在32位操作系统下是4个字节(了解)

6.2、const修饰指针

const修饰指针有三种情况

  1. const修饰指针 — 常量指针
  2. const修饰常量 — 指针常量
  3. const既修饰指针,又修饰常量
  1. int main() {
  2. int a = 10;
  3. int b = 10;
  4. //const修饰的是指针,指针指向可以改,指针指向的值不可以更改
  5. const int * p1 = &a;
  6. p1 = &b; //正确
  7. //*p1 = 100; 报错
  8. //const修饰的是常量,指针指向不可以改,指针指向的值可以更改
  9. int * const p2 = &a;
  10. //p2 = &b; //错误
  11. *p2 = 100; //正确
  12. //const既修饰指针又修饰常量
  13. const int * const p3 = &a;
  14. //p3 = &b; //错误
  15. //*p3 = 100; //错误
  16. system("pause");
  17. return 0;
  18. }
  19. 12345678910111213141516171819202122232425

技巧:看const右侧紧跟着的是指针还是常量, 是指针就是常量指针,是常量就是指针常量

6.3、指针和数组

作用:利用指针访问数组中元素

  • C++规定,数组名就是数组的起始地址
  • 数组的指针就是数组的起始地址
  • 数组名可以作函数的实参和形参,传递的是数组的地址
  1. int main() {
  2. int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
  3. int * p = arr; //指向数组的指针
  4. cout << "第一个元素: " << arr[0] << endl; //1
  5. cout << "指针访问第一个元素: " << *p << endl; //1
  6. for (int i = 0; i < 10; i++)
  7. {
  8. //利用指针遍历数组
  9. cout << *p << endl;
  10. p++;
  11. }
  12. system("pause");
  13. return 0;
  14. }

6.4、指针和函数

作用:利用指针作函数参数,可以修改实参的值(地址传递

  1. //值传递
  2. void swap1(int a ,int b)
  3. {
  4. int temp = a;
  5. a = b;
  6. b = temp;
  7. }
  8. //地址传递
  9. void swap2(int * p1, int *p2)
  10. {
  11. int temp = *p1;
  12. *p1 = *p2;
  13. *p2 = temp;
  14. }
  15. int main() {
  16. int a = 10;
  17. int b = 20;
  18. swap1(a, b); // 值传递不会改变实参
  19. swap2(&a, &b); //地址传递会改变实参
  20. cout << "a = " << a << endl;
  21. cout << "b = " << b << endl;
  22. system("pause");
  23. return 0;
  24. }

  1. int a[10];
  2. int *p = &a[0]; // 等价于 int *p = a;
  3. *p = 1; // 等价于 a[0] = 1;
  4. *(p+1) = 2; // 等价于 a[1] = 2;
  5. // 所以 *(p+1) = a[1]; *(p+2) = a[2];
  6. 123456
  • C++规定, p+1 指向数组的 下一个元素

  1. void main()
  2. {
  3. int array[10];
  4. // 用数组名作形参,因为接收的是地址,所以可以不指定具体的元素个数
  5. f(array,10);
  6. }
  7. // 形参数组
  8. f(int arr[],int n)
  9. {
  10. ....
  11. }

  1. void main()
  2. {
  3. int a[10];
  4. // 实参数组
  5. f(a,10);
  6. }
  7. // 形参指针
  8. f(int *x,int n)
  9. {
  10. ...
  11. }

总结:如果不想修改实参,就用值传递,如果想修改实参,就用地址传递

6.4、返回指针值的函数

  • 返回指针值的函数简称指针函数。
  • 定义指针函数的一般形式为:
  1. // 类型名 * 函数名(参数列表)
  2. int * a(int x,int y);
  3. 12

7、引用

作用: 给变量起别名

语法: 数据类型 &别名 = 原名

  1. int main() {
  2. int a = 10;
  3. int &b = a;
  4. cout << "a = " << a << endl;
  5. cout << "b = " << b << endl;
  6. // 10
  7. // 10
  8. b = 100;
  9. cout << "a = " << a << endl;
  10. cout << "b = " << b << endl;
  11. // 100
  12. // 100
  13. system("pause");
  14. return 0;
  15. }

7.1、引用注意事项

  • 引用必须初始化

    1. int &c; // 错误,引用必须初始化
    2. 1
  • 在声明一个引用后,不能再使之作为另一变量的引用

7.2、引用做函数参数

作用:函数传参时,可以利用引用的技术让形参修饰实参

优点:可以简化指针修改实参

  • 通过引用参数产生的效果同按地址传递是一样的。引用的语法更清楚简单
  1. //1. 值传递
  2. void mySwap01(int a, int b) {
  3. int temp = a;
  4. a = b;
  5. b = temp;
  6. }
  7. //2. 地址传递
  8. void mySwap02(int* a, int* b) {
  9. int temp = *a;
  10. *a = *b;
  11. *b = temp;
  12. }
  13. //参数:把地址传进去,用指针接收
  14. //3. 引用传递
  15. void mySwap03(int& a, int& b) {
  16. int temp = a;
  17. a = b;
  18. b = temp;
  19. }
  20. //参数:别名,下面的a是上面的a的别名,用别名操作修改可原名操作修改是一样的
  21. int main() {
  22. int a = 10;
  23. int b = 20;
  24. // 值传递,形参不会修饰实参
  25. mySwap01(a, b);
  26. cout << "a:" << a << " b:" << b << endl;
  27. // a:10 b:20
  28. // 地址传递,形参会修饰实参
  29. mySwap02(&a, &b);
  30. cout << "a:" << a << " b:" << b << endl;
  31. // a:20 b:10
  32. // 引用传递,形参会修饰实参
  33. mySwap03(a, b);
  34. cout << "a:" << a << " b:" << b << endl;
  35. // a:20 b:10
  36. system("pause");
  37. return 0;
  38. }
  39. 12345678910111213141516171819202122232425262728293031323334353637383940414243444546

7.3、引用做函数返回值

作用:引用是可以作为函数的返回值存在的

  1. //数据类型后加&,相当于用引用的方式返回
  2. int& test02() {
  3. // 必须使用静态变量,需加 static 关键字
  4. static int a = 20;
  5. return a;
  6. }
  7. int main(){
  8. int& ref2 = test02();
  9. system("pause");
  10. return 0;
  11. }

8、类和对象

8.1、struct和class区别

在C++中 struct和class唯一的区别就在于 默认的访问权限不同

区别:

  • struct 默认权限为公共
  • class 默认权限为私有

8.1、构造函数和析构函数

  • 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
  • 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。

构造函数语法:类名(){}构造和析构很容易出选择题,特点要记住

  1. 构造函数,没有返回值也不写void
  2. 构造函数的名字必须与类名相同
  3. 构造函数可以有参数,因此可以发生重载
  4. 程序在调用对象时候会自动调用构造函数,无须手动调用,而且只会调用一次(构造函数不需用户调用,也不能被用户调用)

析构函数语法: ~类名(){}

  1. 析构函数,没有返回值也不写void
  2. 函数名称与类名相同,在名称前加上符号 ~
  3. 析构函数不可以有参数,因此不可以发生重载
  4. 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次
  1. class Person
  2. {
  3. public:
  4. //构造函数
  5. Person()
  6. {
  7. cout << "Person的构造函数调用" << endl;
  8. }
  9. //析构函数
  10. ~Person()
  11. {
  12. cout << "Person的析构函数调用" << endl;
  13. }
  14. };
  15. void test01()
  16. {
  17. Person p; //在栈上的数据,test01()执行完毕后,释放这个对象
  18. }
  19. int main() {
  20. test01();
  21. system("pause");
  22. return 0;
  23. }

8.3、构造函数分类与调用

构造函数按参数分为: 有参构造和无参构造

调用方式:括号法

  1. class Person {
  2. public:
  3. //无参(默认)构造函数
  4. Person() {
  5. cout << "无参构造函数!" << endl;
  6. }
  7. //有参构造函数
  8. Person(int a) {
  9. age = a;
  10. cout << "有参构造函数!" << endl;
  11. }
  12. //析构函数
  13. ~Person() {
  14. cout << "析构函数!" << endl;
  15. }
  16. public:
  17. int age;
  18. };
  19. //2、构造函数的调用
  20. //调用无参构造函数
  21. void test01() {
  22. Person p; //调用无参构造函数
  23. }
  24. //调用有参的构造函数
  25. void test02() {
  26. // 括号法,常用
  27. Person p1(10);
  28. }
  • 尽管在一个类中可以包含多个构造函数,但是对于每一个对象来说,建立对象时只执行其中一个构造函数,并非每个构造函数都被执行

8.4、初始化列表

  • C++提供了初始化列表语法,用来初始化属性

语法:构造函数():属性1(值1),属性2(值2)... {}

  1. class Person {
  2. public:
  3. 传统方式初始化
  4. //Person(int a, int b, int c) {
  5. // m_A = a;
  6. // m_B = b;
  7. // m_C = c;
  8. //}
  9. //初始化列表方式初始化
  10. Person(int a, int b, int c) :m_A(a), m_B(b), m_C(c) {}
  11. void PrintPerson() {
  12. cout << "mA:" << m_A << endl;
  13. cout << "mB:" << m_B << endl;
  14. cout << "mC:" << m_C << endl;
  15. }
  16. private:
  17. int m_A;
  18. int m_B;
  19. int m_C;
  20. };
  21. int main() {
  22. Person p(1, 2, 3);
  23. p.PrintPerson();
  24. system("pause");
  25. return 0;
  26. }

8.5、类对象作为类成员

C++类中的成员可以是另一个类的对象,我们称该成员为 对象成员

例如:

  1. class A {}
  2. class B
  3. {
  4. A a
  5. }
  • B类中有对象A作为成员,A为对象成员
  • 那么当创建B对象时,A与B的构造和析构的顺序是谁先谁后?
    • 先调用对象成员的构造,再调用本类构造(如上例中,先调用A的构造函数)
    • 析构顺序与构造相反

8.6、静态成员

静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员

静态成员分为:

  • 静态成员变量
    • 所有对象共享同一份数据
    • 在编译阶段分配内存
    • 类内声明,类外初始化
  • 静态成员函数
    • 所有对象共享同一个函数
    • 静态成员函数只能访问静态成员变量
      **示例1 **:静态成员变量
  1. class Person
  2. {
  3. public:
  4. static int m_A; //静态成员变量
  5. //静态成员变量特点:
  6. //1 在编译阶段分配内存
  7. //2 类内声明,类外初始化
  8. //3 所有对象共享同一份数据
  9. private:
  10. static int m_B; //静态成员变量也是有访问权限的
  11. };
  12. int Person::m_A = 10;
  13. int Person::m_B = 10;
  14. void test01()
  15. {
  16. //静态成员变量两种访问方式
  17. //1、通过对象
  18. Person p1;
  19. p1.m_A = 100;
  20. cout << "p1.m_A = " << p1.m_A << endl;
  21. Person p2;
  22. p2.m_A = 200;
  23. cout << "p1.m_A = " << p1.m_A << endl; //共享同一份数据
  24. cout << "p2.m_A = " << p2.m_A << endl;
  25. //2、通过类名
  26. cout << "m_A = " << Person::m_A << endl;
  27. //cout << "m_B = " << Person::m_B << endl; //私有权限访问不到
  28. }
  29. int main() {
  30. test01();
  31. system("pause");
  32. return 0;
  33. }

示例2:静态成员函数

  1. class Person
  2. {
  3. public:
  4. //静态成员函数特点:
  5. //1 程序共享一个函数
  6. //2 静态成员函数只能访问静态成员变量
  7. static void func()
  8. {
  9. cout << "func调用" << endl;
  10. m_A = 100;
  11. //m_B = 100; //错误,不可以访问非静态成员变量
  12. }
  13. static int m_A; //静态成员变量
  14. int m_B; //
  15. private:
  16. //静态成员函数也是有访问权限的
  17. static void func2()
  18. {
  19. cout << "func2调用" << endl;
  20. }
  21. };
  22. int Person::m_A = 10;
  23. void test01()
  24. {
  25. //静态成员变量两种访问方式
  26. //1、通过对象
  27. Person p1;
  28. p1.func();
  29. //2、通过类名
  30. Person::func();
  31. //Person::func2(); //私有权限访问不到
  32. }
  33. int main() {
  34. test01();
  35. system("pause");
  36. return 0;
  37. }

8.7、const修饰成员函数

常函数:

  • 成员函数后加const后我们称为这个函数为常函数
  • 常函数内不可以修改成员属性

常对象:

  • 声明对象前加const称该对象为常对象
  • 常对象只能调用常函数

8.8、const型数据小结

形式 含义
Time const t1 t1 是常对象,其值在任何情况下都不能改变
void Time::fun() const fun 是 Time类中的常成员函数,可以引用,但不能修改本类中的数据成员
Time * const p p 是指向Time对象的常指针,P的值不能改变
const Time *p p是指向 Time 类常对象的指针,其指向的类对象的值不能通过指针来改变
Time &t1 = t; t1是Time类对象t的引用,二者指向同一段内存空间

9、继承

继承的好处:可以减少重复的代码

继承的语法:class 子类 : 继承方式 父类

class A : public B;

A 类称为子类 或 派生类

B 类称为父类 或 基类

9.1、继承方式

继承方式一共有三种:

  • 公共继承
  • 保护继承
  • 私有继承

在这里插入图片描述

简单的说:

  • 公共继承:基类的公用成员和保护成员在派生类中保持原有访问属性,其私有成员仍为基类私有。
  • 私有继承:基类的公用成员和保护成员在派生类中成了私有成员。其私有成员仍为基类私有
  • 保护继承:基类的公用成员和保护成员在派生类中成了保护成员,其私有成员仍为基类私有。

9.2、构造和析构函数

  • 构造函数的主要作用是对数据成员初始化

  • 派生类是不能继承基类的析构函数,也需要通过派生类的析构函数去调用基类的析构函数

  • 继承中 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反

9.3、不能继承

  • C++中,不能被派生类继承的是:构造函数

9.4、继承特点

单继承:一个派生类只从一个基类派生

多继承:一个派生类有两个或多个基类的称为多重继承

10、多态

多态性是指具有不同功能的函数可以用同一个函数名,这样就可以用一个函数名调用不同内容的函数

多态分为两类

  • 静态多态: 函数重载 和 运算符重载属于静态多态,复用函数名
  • 动态多态: 派生类和虚函数(virtual function)实现运行时多态

10.1、虚函数

什么是虚函数?

  • 在基类用 virtual 声明成员函数为虚函数

虚函数的作用

  • 虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数

虚函数的使用方法

  • 在基类用 virtual 声明成员函数为虚函数。这样就可以在派生类中重新定义此函数,为它赋予新的功能,并能方便地被调用
    • 在类外定义虚函数时,不必再加virtual
  • 在派生类中重新定义此函数,要求函数名、函数类型、函数参数个数和类型全部与基类的虚函数相同,并根据派生类的需要重新定义函数体

C++规定,当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数。因此在派生类重新声明该虚函数时,可以加virtual,也可以不加,但习惯上一般在每一层声明该函数时都加virtual,使程序更加清晰

  1. class Animal
  2. {
  3. public:
  4. //Speak函数就是虚函数
  5. //函数前面加上virtual关键字,变成虚函数,那么编译器在编译的时候就不能确定函数调用了。
  6. virtual void speak()
  7. {
  8. cout << "动物在说话" << endl;
  9. }
  10. };
  11. class Cat :public Animal
  12. {
  13. public:
  14. void speak()
  15. {
  16. cout << "小猫在说话" << endl;
  17. }
  18. };
  19. class Dog :public Animal
  20. {
  21. public:
  22. void speak()
  23. {
  24. cout << "小狗在说话" << endl;
  25. }
  26. };
  27. //我们希望传入什么对象,那么就调用什么对象的函数
  28. //如果函数地址在编译阶段就能确定,那么静态联编
  29. //如果函数地址在运行阶段才能确定,就是动态联编
  30. void DoSpeak(Animal & animal)
  31. {
  32. animal.speak();
  33. }
  34. //
  35. //多态满足条件:
  36. //1、有继承关系
  37. //2、子类重写父类中的虚函数
  38. //多态使用:
  39. //父类指针或引用指向子类对象
  40. void test01()
  41. {
  42. Cat cat;
  43. DoSpeak(cat);
  44. Dog dog;
  45. DoSpeak(dog);
  46. }
  47. int main() {
  48. test01();
  49. system("pause");
  50. return 0;
  51. }

多态满足条件

  • 有继承关系
  • 子类重写父类中的虚函数
  1. 纯虚函数
  • 纯虚函数是在声明虚函数时被“初始化”为0的函数。声明纯虚函数的一般形式是

    1. virtual 函数类型 函数名 (参数表列) =0;
    2. 1
  • 纯虚函数没有函数体

  • 最后面的 =0 ,并不表示函数返回值为0,它只告诉编译系统“老子是纯虚函数”

  • 纯虚函数只有函数的名字而不具备函数的功能,不能被调用

  1. 抽象类
  • 凡是包含纯虚函数的类都是抽象类

  • 一个基类如果包含一个或一个以上纯虚函数,就是抽象基类。抽象基类不能也不必要定义对象、

11、文件操作

C++中对文件操作需要包含头文件 < fstream >

文件类型分为两种

  1. 文本文件 - 文件以文本的ASCII码形式存储在计算机中
  2. 二进制文件 - 文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们

操作文件的三大类:

  1. ofstream:写操作
  2. ifstream: 读操作
  3. fstream : 读写操作

11.1、文本文件

5.1.1写文件

写文件步骤如下:

  1. 包含头文件

    include

  2. 创建流对象

    ofstream ofs;

  3. 打开文件

    ofs.open(“文件路径”,打开方式);

  4. 写数据

    ofs << “写入的数据”;

  5. 关闭文件

    ofs.close();

文件打开方式:

打开方式 解释
ios::in 为读文件而打开文件
ios::out 为写文件而打开文件
ios::ate 初始位置:文件尾
ios::app 追加方式写文件
ios::trunc 如果文件存在先删除,再创建
ios::binary 二进制方式

注意: 文件打开方式可以配合使用,利用|操作符

**例如:**用二进制方式写文件 ios::binary | ios:: out

示例:

  1. #include <fstream>
  2. void test01()
  3. {
  4. ofstream ofs;
  5. ofs.open("test.txt", ios::out);
  6. ofs << "姓名:张三" << endl;
  7. ofs << "性别:男" << endl;
  8. ofs << "年龄:18" << endl;
  9. ofs.close();
  10. }
  11. int main() {
  12. test01();
  13. system("pause");
  14. return 0;
  15. }

总结:

  • 文件操作必须包含头文件 fstream
  • 读文件可以利用 ofstream ,或者fstream类
  • 打开文件时候需要指定操作文件的路径,以及打开方式
  • 利用<<可以向文件中写数据
  • 操作完毕,要关闭文件

5.1.2读文件

读文件与写文件步骤相似,但是读取方式相对于比较多

读文件步骤如下:

  1. 包含头文件

    include

  2. 创建流对象

    ifstream ifs;

  3. 打开文件并判断文件是否打开成功

    ifs.open(“文件路径”,打开方式);

  4. 读数据

    四种方式读取

  5. 关闭文件

    ifs.close();

示例:

  1. #include <fstream>
  2. #include <string>
  3. void test01()
  4. {
  5. ifstream ifs;
  6. ifs.open("test.txt", ios::in);
  7. if (!ifs.is_open())
  8. {
  9. cout << "文件打开失败" << endl;
  10. return;
  11. }
  12. //第一种方式
  13. //char buf[1024] = { 0 };
  14. //while (ifs >> buf)
  15. //{
  16. // cout << buf << endl;
  17. //}
  18. //第二种
  19. //char buf[1024] = { 0 };
  20. //while (ifs.getline(buf,sizeof(buf)))
  21. //{
  22. // cout << buf << endl;
  23. //}
  24. //第三种
  25. //string buf;
  26. //while (getline(ifs, buf))
  27. //{
  28. // cout << buf << endl;
  29. //}
  30. char c;
  31. while ((c = ifs.get()) != EOF)
  32. {
  33. cout << c;
  34. }
  35. ifs.close();
  36. }
  37. int main() {
  38. test01();
  39. system("pause");
  40. return 0;
  41. }

总结:

  • 读文件可以利用 ifstream ,或者fstream类
  • 利用is_open函数可以判断文件是否打开成功
  • close 关闭文件

5.2 二进制文件

以二进制的方式对文件进行读写操作

打开方式要指定为 ios::binary

5.2.1 写文件

二进制方式写文件主要利用流对象调用成员函数write

函数原型 :ostream& write(const char * buffer,int len);

参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数

示例:

  1. #include <fstream>
  2. #include <string>
  3. class Person
  4. {
  5. public:
  6. char m_Name[64];
  7. int m_Age;
  8. };
  9. //二进制文件 写文件
  10. void test01()
  11. {
  12. //1、包含头文件
  13. //2、创建输出流对象
  14. ofstream ofs("person.txt", ios::out | ios::binary);
  15. //3、打开文件
  16. //ofs.open("person.txt", ios::out | ios::binary);
  17. Person p = {"张三" , 18};
  18. //4、写文件
  19. ofs.write((const char *)&p, sizeof(p));
  20. //5、关闭文件
  21. ofs.close();
  22. }
  23. int main() {
  24. test01();
  25. system("pause");
  26. return 0;
  27. }

总结:

  • 文件输出流对象 可以通过write函数,以二进制方式写数据

5.2.2 读文件

二进制方式读文件主要利用流对象调用成员函数read

函数原型:istream& read(char *buffer,int len);

参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数

示例:

  1. #include <fstream>
  2. #include <string>
  3. class Person
  4. {
  5. public:
  6. char m_Name[64];
  7. int m_Age;
  8. };
  9. void test01()
  10. {
  11. ifstream ifs("person.txt", ios::in | ios::binary);
  12. if (!ifs.is_open())
  13. {
  14. cout << "文件打开失败" << endl;
  15. }
  16. Person p;
  17. ifs.read((char *)&p, sizeof(p));
  18. cout << "姓名: " << p.m_Name << " 年龄: " << p.m_Age << endl;
  19. }
  20. int main() {
  21. test01();
  22. system("pause");
  23. return 0;
  24. }
  • 文件输入流对象 可以通过read函数,以二进制方式读数据

12、输入输出流

  • 标准输出流:cout 、cerr、clog

  • cerr、clog流对象都是标准错误流,都是在终端显示器上显示出错信息

  • 标准输入流:cin、get、getline

  • get函数读入一个字符,getline读入一行字符

  • eof函数:文件结束

  • peek函数:peek函数的作用是观测下一个字符。

  • putback函数:其作用是将前面用get或getline函数从输入流中读取的字符ch返回到输入流,插入到当前指针位置,以供后面读取

  • ignore函数:函数作用是跳过输入流中n个字符,或在遇到指定的终止字符时提前结束

13、异常

C++处理异常的机制是由3个部分组成的,即检查(try)、抛出(throw)和捕捉(catch)

命名空间的作用是建立一些互相分隔的作用域,把一些全局实体分隔开来,以免产生名字冲突