写在前面

由于我也是第一次学习C++,很多概念我自己也不清楚,所以此处基本上用来记录我学习过程中写的代码
各类概念和知识点理论,则是想到多少学到多少记多少。

C++基础部分

输出helloworld代码

1
2
3
4
5
6
7
8
9
10
11
#include<iostream>
using namespace std;

int main()
{
cout << "hello world" << endl;
system("pause");
return 0;
}


第六行cout语句实现了输出的功能,除去第六行的语句,整体就是C++代码的基本框架
类似java中的类体+方法体

注释

C++的注释有以下几种

单行注释

//

多行注释

/**/
同样的,编译器编译代码的时候,会忽略注释的内容。

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
//#include <iostream>
///*这个include相当于import的意思
//相当于是导入了iostream这个包
//然后这个iostream是c++中进行输入输出操作的标准库
//*/
//using namespace std;
///*这条指令是告诉编译器 程序要使用std中的所有内容
//* 相当于是java lang类 如果不导入
//* 每次输入输出都要加上std 代码会变得很冗长
//* 所以这里导入std
//*/
//
//int main() {
///*这里就是普通的定义了一个main方法
//和Java类似 但是没有public static那一套
//然后这里下面的return 0 表示了程序正常结束
//在C++中程序正常结束就是返回0*/
// cout << "爷回来辣" << endl;
// system("pause");
// /*system() 是 C 标准库中的一个函数
// 它的参数是一个字符串,表示要执行的命令。
// 用来暂停程序的执行并显示提示信息
// 这个命令的作用是让用户有时间查看程序输出的内容
// 而不会让控制台窗口在程序结束后立刻关闭*/
// return 0;
//}

变量和常量

变量基本介绍

变量存在的意义: 管理内存空间
变量创建语法:数据类型 变量名 = 变量初始值;

1
2
3
4
5
6
7
8
9
10
11
12
13
int a = 10
#include <iostream>
using namespace std;
int main() {
int a = 1;
cout << "a=" << a << endl;
/*cout的意思就是把对象输出到控制台
<<就是把右侧的内容插入到左边*/

system("pause");
//这里system("pause")的作用是运行完了之后暂停窗口
//方便你查看 结果 就不会直接结束程序
return 0;

常量

c++中和java中不一样 还存在常量,常量就是定义之后不可以发生改变的数据
可以看成java中用final修饰的变量
定义的两种方法
1、用#define定义在文件的上方 作为宏常量

1
#define 常量名 常量值

2、用const修饰一个变量
const 数据类型 数据名 = 数据值
两种方法定义的常量都不可以发生改变 如果尝试在代码中改变就会报错

关于const:我认为的const本质:限定修改

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

//#define day 7;
/*上方代码报错
为什么 因为用#defin 定义常量 不需要使用分号结尾
按下面的方式定义就正确了*/
#define day 7
int main() {
//day = 14;
/*上面的代码会报错 因为常量不能被改变*/
/*const 修饰的变量*/
const int month = 12;
//month = 24;
/*同样报错 用const修饰的变量也会变成常量*/
cout << "a year have " << month << " months" << endl;
cout << "a week have " << day << " days" << endl;
system("pause");
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
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
#include <iostream>
using namespace std;

#include<string>
/*可以理解为导入string 的包
* 再用C++风格的字符串的时候 要包含这个头文件
*/

/*C++的数据类型*/


int main() {

/* 整型
C++的整型分成
短整型 short 最小的表述范围都特别大 +-3w多
整型 int
长整型 long
长长整型 longlong*/
short num1 = 10;
int num2 = 10;
long num3 = 10;
long long num4 = 10;

cout << "num1 = " << num1 << endl;
cout << "num2 = " << num2 << endl;
cout << "num3 = " << num3 << endl;
cout << "num4 = " << num4 << endl;


/*size of 关键字
通过这个关键字可以求出数据类型的所占的内存大小*/

cout << "num4 的大小: " <<sizeof(num4) << endl;
cout << "short 的大小: " <<sizeof(short) << endl;

/*实型(浮点型
作用:表示小数
C++中有两种浮点型
单精度 float
双进度 double
双精度的有效范围更大*/
/*float的定义*/
float f1 = 3.14f;
/*为什么这里用3.14f而不用3.14
因为程序运行的逻辑是先创建了3.14 再通过指针
把f1指向这个3.14的位置 然后在程序创建3.14的时候会默认创建为一个double
的数据 所以要在后面先加上f 让他在创建的时候就创建为float的


上面的理解是全部错误的
cpp中将没有后缀的浮点数全部默认解释为double 因为double有更高的精度和更大的表数范围*/
cout << "f1 = " << f1 << endl;
cout << "f1 的大小: " << sizeof(f1) << endl;

double d1 = 3.14;

cout << "d1 = " << f1 << endl;
cout << "d1 的大小: " << sizeof(d1) << endl;



/*科学计数法*/
float f2 = 3e2;
/*3*10^2
*/
cout << "f2 = " << f2 << endl;
cout << "f2 的大小: " << sizeof(f2) << endl;

float f3 = 3e-2;
cout << "f3 = " << f3 << endl;
cout << "f3 的大小: " << sizeof(f3) << endl;

/*字符型*/
/*字符型就是表示单个字符的数据类型
下面依次 写
字符型变量的创建方式
所占的内存大小
常见错误
对应的ASCII编码

*/
char ch1 = 'a';
cout << "ch1 = " << ch1 << endl;
cout << "ch1 的大小: " << sizeof(ch1) << endl;
cout << "ch1 " << ch1 << endl;

/*加入字符的时候用单引号应用
这点在java里面其实也是 一样 的
java中的双引号是用来引入字符串string的
string和char有本质的区别 所以确实不同
字符型只占用一个字节
字符型变量并不是把字符本身放到内存 中存储
而是讲ASCII编码放到存储单元中
*/

/*转义字符
作用 用于表示一些不能显示出来的ASCII字符
现阶段我们常用的转义字符 有 \n \\ \t
*/
cout << "转义字符\\ n " << "\n" << endl;
cout << "hello\n world " << endl;
cout << "转义字符\\ t " << "\t" << endl;
cout << "转义字符\\ \ " << "\\" << endl;

/*在c中没有endl 结束语句也是通过\n*/
cout << "hello world \n";
/*上面的代码可以执行*/

/*水平制表符\t
水平制表符具体输出 几个空格并不确定
取决于一种叫制表位的概念
一般来说是八个字符占一个制表位置
所以输出 的时候 就是输出到最开始的八个字符 之后
如果已经输出了 3个字符 那么他就会输出到五个字符 的后面*/

cout << "aaa\t hello world " << endl;
cout << "aaaa\t hello world " << endl;
cout << "aaaaa\t hello world " << endl;
cout << "aaaaaa\t hello world " << endl;
cout << "aaaaaaa\t hello world " << endl;

/*字符串类型
字符串类型分为两种 C类型和C++类型
C类型的本质我认为就是一个字符的数组
C风格 char 变量名[] = "字符串值"
C++ 风格字符串 string 变量名 = "字符串值"*/
char str1[] = "hello world";
cout <<"str1:" << str1 << endl;
/*注意事项1、一定要加[]表示数组
2、一定要用 双引号 因为是多个字符 */

string str2 = "hello world";
cout << "str2:" << str2 << endl;
/*注意事项1、 C++中的string 是小写S开头
java中是大写s开头
2、一定要导入include string*/

/*布尔数据类型
只有真假 true false
本质就是0 和1 所以只占1字节
创建布尔类型数据
查看所占用的空间
*/

bool bl1 = true;;
bool bl2 = false;;
cout << "bl1 = " << bl1 << endl;
cout << "所占字节: " << sizeof(bl1) << endl;
cout << "bl1 = " << bl2 << endl;
cout << "所占字节 " << sizeof(bl1) << endl;
/*本质上1代表真 0带代表假*/
system("pause");
return 0;
}


/*关于c++的命名规则
1、不能是关键字
2、字母、数字、下划线组成
3、第一个字符不能是数字
4、区分大小写

和java的主要区别
1、java中可以使用美元符号,且可以用$作为开头*/

数据的输入

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
#include <iostream>
#include<string>
using namespace std;



int main() {

/*数据的输入
使用关键字 cin
使用语法 cin >> 变量;*/

/*依次输入整型浮点型字符型字符串布尔型等*/
int a1 = 0;
cout << "请给整型变量a1赋值:" << endl;
cin >> a1;
cout << "现在a的值:" << a1<<endl;

double a2 = 0.0;
cout << "请给浮点型a2赋值:" << endl;
cin >> a2;
cout << "现在a2的值:" << a2 << endl;

char a3 = '0';
cout << "请给字符型a3赋值:" << endl;
cin >> a3;
cout << "现在a3的值:" << a3 << endl;

string a4 = "0";
cout << "请给字符串型a4赋值:" << endl;
cin >> a4;
cout << "现在a4的值:" << a4 << endl;

bool a5 = 0;
cout << "请给布尔型a5赋值:" << endl;
cin >> a5;
cout << "现在a5的值:" << a5 << endl;
system("pause");
/*布尔类型的值赋值 只要是非0的值 都代表真*/

+



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
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
#include<iostream>
#include<string>
using namespace std;

int main() {


/*基本运算符
+- * /%
++ -- */
int n1 = 0;
int n2 = 0;

cout << "给 整型数据 n1赋值:" << endl;
cin >> n1;
cout << "给 整型数据 n2赋值:" << endl;
cin >> n2;

int n3 = n1;
int n4 = n2;

cout << "加:" << n1 + n2 << endl;
cout << "减:" << n1 - n2 << endl;
cout << "乘:" << n1 * n2 << endl;
cout << "除:" << n1 / n2 << endl;
cout << "取余:" << n1 % n2 << endl;

n1 = ++n2;
cout << "n1 = ++n2 " <<"n1:"<<n1<<"n2:" << n2 << endl;
/*等号右边的代码从左往右运行 首先先自增++ n2 = n2+1
n2本来是15 现在是16 然后赋值给n1 所以n1 = n2 = 16*/

n1 = n3;
n2 = n4;
n1 = n2++;
cout << "n1 =n2++ " << "n1:" << n1 << "n2:" << n2 << endl;
/*这里就是先赋值再加减 先是n1 = n2 = 15 然后n2 + 1 = 16
下面的减法代码也是同理*/


n1 = n3;
n2 = n4;
n1 = --n2;
cout << "n1 = --n2 " << "n1:" << n1 << "n2:" << n2 << endl;

n1 = n3;
n2 = n4;
n1 = n2--;
cout << "n1 = n2-- " << "n1:" << n1 << "n2:" << n2 << endl;

int m1 = 10;
float m2 = 3.14;
float m3 = 3.1415926535f;
double m4 = 3.1415926535;

cout << m1 / m2 << endl;
cout << m3 << endl;
cout << m4 << endl;

/*关于除法
1、除数不能为0
2、不能对0取余*/


/*赋值运算符*/
int z1 = 10;
int z2 = z1 += 1;

cout << "z1+=1:" << z2 << endl;
z2 = z1 -= 1;
cout << "z1-=1:" << z2 << endl;

/*这里把加号换成- * / % 都是同理
所以就不逐个演示了
下面讲一讲与或非
符号分别是!非
&& 与
|| 或 */

n1 = 0;
n2 = 1;

cout << "n1 = " << n1 << "/n" << "n2 = " << n2 << endl;

bool z3 = (n1 && n2);
cout << "n1 && n2:" << z3 << endl;

z3 = (n1 || n2);
cout << "n1 || n2:" << z3 << endl;




system("pause");
return 0;

}

循环结构和分支结构

if分支

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<string>

using namespace std;

/*程序执行的三种结构
顺序结构 就是直接按照顺序执行代码
选择结构 就是执行满足条件 的代码
循环结构 满足条件时重复执行代码 直到不满足条件为止*/

int main() {

/*if 语句
输入分数 大于100 则打印考上了*/
int score = 0;
/*在Cpp中变量不会默认初始化,所以你定义完了一个数字之后必须要赋值一个值
这点和java不同*/
cout << "please enter a score" << endl;
cin >> score;

cout << "you score is:" <<score<< endl;

if (score > 100)
{
cout << "you can go to the college!" << endl;
}
else if (score > 60)
{
cout << "Maybe next time" << endl;
}
else {
cout << "stupid" << endl;
}




system("pause");
return 0;
}

if循环练习

三只小猪称体重,找出最大的那一只

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

using namespace std;

/*一个小程序 三只小猪比体重*/

int main() {

int p1 = 0;
int p2 = 0;
int p3 = 0;

cout << "请输入第一只小猪的体重 \n ";
cin >> p1;
cout << "第一只小猪的体重是:" << p1 << endl;

cout << "请输入第二只小猪的体重 \n ";
cin >> p2;
cout << "第二只小猪的体重是:" << p2 << endl;

cout << "请输入第三只小猪的体重 \n ";
cin >> p3;
cout << "第三 只小猪的体重是:" << p3<< endl;

if (p1 > p2)
{
if (p1 > p3) {
cout << "最重的是第一只小猪,重量为:" << p1 << endl;
}
else
{
cout << "最重的是第三只小猪,重量为:" << p3 << endl;
}
}
else
{
if (p2 > p3)
{
cout << "最重的是第二只小猪,重量为:" << p2 << endl;
}
else {
cout << "最重的是第三只小猪,重量为:" << p3 << endl;
}
}


system("pause");
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
#include <iostream>
#include<string>
using namespace std;

/*三目运算符
表达式一 ? 表达式二 : 表达式3
对表达式1判断 真执行2假执行3*/

int main() {

/*三目运算符
创建abc变量
ab比较 大的赋值c*/

int a = 10;
int b = 20;
int c = 0;

c= (a > b ? a : b);
cout << c << endl;

/*在cpp中 三目运算符返回的是变量,可以继续赋值*/
(a > b ? a : b) = 100;
/*返回 的值是b
这里是把返回的b重新赋值了*/

cout <<"a:" << a << endl;
cout <<"b:" << b << endl;


system("pause");

return 0;
}

switch和do while基本语法

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

#include<ctime>
/*time系统时间头文件包含*/

using namespace std;

/*switch 语句
switch基本语法
switch( 表达式){
case 结果1: 执行语句 ;break;
default:执行语句;break;
}
根据表达式产生的结果不同
执行 不同 的 语句,
*/
int main() {

/*给电影进行打分
10-9 经典
8-7 非常好
6-5 一般
5以下 烂片*/

short int a1 = 0;
cout << "please enter your score:" << endl;
//cin >> a1;
a1 = 1;

//switch (a1)
//{
//case 10:
// cout << "经典" << endl;
// break;
//case 9 :
// cout << "经典" << endl;
// break;
//case 8:
// cout << "非常好" << endl;
// break;
//case 7:
// cout << "非常好" << endl;
// break;
//case 6:
// cout << "一般" << endl;
// break;
//case 5:
// cout << "一般" << endl;
// break;

//default:
// cout << "不行" << endl;
// break;
//}


switch (a1)
{
case 10:
case 9:
cout << "经典" << endl;
break;
case 8:
case 7:
cout << "非常好" << endl;
break;
case 6:
case 5:
cout << "一般" << endl;
break;

default:
cout << "不行" << endl;
break;
}

/*循环 结构
while循环
*/

int n1 = 0;
while (n1 < 10) {
cout << n1 << endl;
n1++;
}

srand((unsigned int)time(NULL));
/*猜数字游戏*/
int a2 = (rand() % 100) + 1;
int b1 = (rand() % 100) + 1;
cout << a2 << endl;
cout << b1 << endl;
int b2 = (rand() % 100) + 1;
cout << b2 << endl;
/*这个函数可以随机生成 一个不小于后面数字的 数
这里 就是随机生成了一个 0-99之间 的 数*/
/*然后发现这个随机数不是真的随机数 每次生成的都是一样
重复生成的数字也是固定的
然后看到网上说要添加随机数的种子,才能防止每次生成的都一样
可以根据系统时间生成随机数*/


int a3 = 0;
cout << "enter your guess:" << endl;
cin >> a3;
int a4 = 0;
while (1) {
if (a3 > a2)
{
cout << "a2 is smaller" << endl;
cin >> a3;
a4++;
}
else if (a3 < a2) {
cout << "a2 is bigger" << endl;
cin >> a3;
a4++;
}
else
{
cout << "you god damn right" << endl;
cout << "you guess " << a4 << " times" << endl;
break;
}
}

/*do while 循环语句
dowhile先 执行一次 循环语句
再判断 循环条件
*/
int n2 = 0;
do {
cout << n2 << endl;
n2++;
} while (n2 < 10);

system("pause");
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
#include <iostream>
#include <string>
using namespace std;

/*水仙花数练习
解题思路
1、输出所有三位数 (100-999)
2、找到水仙花数字
3、取模取各个位

判断 个位^3+十位^3+百位^3 = 本身

*/

int main() {

/*打印三位数*/
int num = 100;

do{
/*添加判断条件 如果是水仙花数才打印*/
int a1 = 0;
int a2 = 0;
int a3 = 0;

a1 = num % 10;
a2 = (num/10)%10;
a3 = num/100;

int b1 = a1 * a1 * a1;
int b2 = a2 * a2 * a2;
int b3 = a3 * a3 * a3;

int c1 = b1 + b2 + b3;
if ( c1 == num )
{
cout << num << endl;
}
num++;
}
while (num < 1000);


system("pause");

return 0;
}

for循环

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>
#include <string>
#include <ctime>

using namespace std;
/*for 循环
满足循环条件时
执行循环语句
语法
for(起始表达式;条件表达式;末尾循环体){
循环语句 }
起始表达式不参与循环,
条件表达式就是循环执行的条件
末尾循环体在循环每次结束之后运行
*/
int main() {
/*用for循环从数字0打印到9*/
for (int i = 0; i < 10; i++)
{/*需要声明变量的类型
java和c++中都需要声明*/
cout << i << endl;

}

int a = 0;
for (; a < 10; a++)
{/*声明也可以写外面
但是声明如果写在外面,不管写不写起始表达式都要加一个分号*/
cout << a << endl;

}


system("pause");

return 0;
}

for循环练习案例

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>
#include<string>
#include<ctime>

using namespace std;
/*练习案例 敲桌子
从数字1数到100 如果数字个位
十位 含有7或者是7的倍数 我们打印敲桌子
其他数字直接打印*/
int main() {

for (int i = 1; i < 100; i++) {
int a = (i % 10);
int b = i / 10;

if (a == 7)
{
cout << "flap desk" << endl;
}
else if(b == 7){
cout << "flap desk" << endl;
}
else if (i % 7 ==0)
{
cout << "flap desk" << endl;
}
else {
cout << i << endl;
}
}




system("pause");
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
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

/*嵌套循环*/

int main() {

/*练习1 打印10x10图*/
for (int i = 0; i < 11; i++) {
for (int j = 0;j<11; j++)
{
cout << " *" ;
}
cout << endl;
}


system("pause");
return 0;
}

打印乘法口诀表

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

/*打印乘法口诀表*/
using namespace std;

int main() {

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

system("pause");
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
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

/*跳转语句
1、出现在switch语句中 跳出循环
2、出现在循环中,作用是跳出当前的循环
3、出现在嵌套循环中,跳出内层的循环*/
int main() {

cout << "please choice level" << endl;
cout << "1、easy" << endl;
cout << "2、normal" << endl;
cout << "3、difficult" << endl;

int select = 0;

cin >> select;

switch (select)
{
case 1:
cout << "you choice easy mode" << endl;
break;
case 2:
cout << "you choice normal mode" << endl;
break;
case 3:
cout << "you choice difficult mode" << endl;
break;

default:
break;
}

/*出现在for循环中*/
for (int i = 0; i < 10; i++)
{
if (i == 5)
{
break;
}

cout << i << endl;

}

/*嵌套循环*/
for (int i = 0; i < 11; i++)
{

for (int j = 0; j < 11;j++) {
if (j==5)
{
break;
}

cout << " *";
}
cout << endl;
}



system("pause");

return 0;
}

数组

一维数组

一维数组定义的三种方法

数据类型 数据名 [数组长度];
数据类型 数据名 [数组长度] = {值1,值2};
数据类型 数据名 [] = {值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
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

/*数组
数组就是一个集合 里面装了相同类型的数据元素

特点1 数组中的每个数据元素都是相同的数据类型
特点2 数组是由连续的内存位置组成的*/

int main() {

/*一维数组的三种定义方式
2、数据类型 数据名 [数组长度];
首先根据特点1 数组里面所有元素都需要是相同的数据类型
所以声明数据类型特别重要
数据名就是数组的总体名称
最后因为数组是一个定长 同时内存位置连续
所以需要声明数组长度 提前规划数组内存位置
2、数据类型 数据名 [数组长度] = {值1,值2};
3、数据类型 数据名 [] = {值1,值2};
第三种方法是你直接 把值写入进去 然后不人为定义的数组长度
那么他会根据你写入的值默认定义数组的长度

数组自动生成下标 我们可以通过下标来访问数组中的元素

*/

/*第一种方法 */
int arr1[5];
/*给数组中的元素进行赋值
数组下标从0开始*/
arr1[0] = 10;
arr1[1] = 20;
arr1[2] = 30;
arr1[3] = 40;
arr1[4] = 50;

/*访问数据元素*/
cout << arr1[0] << endl;

/*第二种*/
int arr2[5] = { 10,20,30,40,50 };
//cout << arr2[0] << endl;

/*利用循环的方式输出*/
for (int i = 0; i < 5; i++)
{
cout << arr2[i] << endl;
}

int arr3[5] = { 10,20,30};
cout << arr3[4] << endl;
/*如果在初始化的时候没有填写完全部的数据
,剩下的数据会用0来填补*/

/*定义数组的时候必须有数组初始长度*/
int arr4[] = { 9,8,7,6,5,4,3,2,1 };

for (int i = 0; i < 9; i++)
{
cout << arr4[i] << endl;
}


system("pause");

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

using namespace std;

/*一维数组数组名的用途
1、统计整个数组在内存中的长途*/

int main() {

int arr1[5] = { 10,20,30,40,50 };
cout << sizeof(arr1[0]) << endl;
cout << sizeof(arr1) << endl;
cout << sizeof(arr1)/ sizeof(arr1[0]) << endl;

/*输出数组名称查看首地址*/
cout << arr1 << endl;
cout << &arr1[0] << endl;

cout << (int)arr1 << endl;
cout << (int) & arr1[0] << endl;
cout << (int) & arr1[1] << endl;


arr1[0] = 20;
cout << arr1[0] << endl;

system("pause");

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

using namespace std;

/*五只小猪称重
一个数组中记录了五只小猪的体重
从中选出最重的*/

int main() {

int pig[5] = { 100,300,500,200,400 };
int j = 0;
for (int i = 0; i < 5; i++)
{
if (pig[i] > j)
{
j = pig[i];
}
else
{
continue;
}

}
cout << j << endl;
system("pause");

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

/*冒泡排序*/
/*最常用的排序算法
对数组内的元素进行排序
比较相邻的元素,如果第一个比第二个大,就交换
对比每一对相邻元素做同样的工作,执行完毕后,找到第一个最大值
重复以上的步骤,每次比较次数-1直到不需要比较
总的比较次数 = 数组长度 - 1
数组长度 用size of可以计算

*/

//void swapValues(int& a, int& b) {
// int temp = a; // 使用临时变量来交换
// a = b;
// b = temp;
//}

int main() {

int arr1[] = { 4,9,8,0,5,7,1,3,2 };

/*计算数组长度*/
int length = sizeof(arr1) / sizeof(arr1[0]);
cout << "数组长度" << length << endl;
int times = length - 1;

int tempo = 0;

/*排序总轮数 = 元素个数 -1
每轮对比的次数 = 元素个数 - 排序轮数*/

//总排序轮数 times
for (int i = 0; i < times; i++)
{
for (int j = 0; j < times - i; j++)
{
if (arr1[j] > arr1[j+1])
{
tempo = arr1[j];
arr1[j] = arr1[j + 1];
arr1[j + 1] = tempo;
}
}
}

cout << "冒泡排序 排序后" << endl;
for (int i = 0; i < length; i++)
{
cout << arr1[i];
}
cout << endl;



/*调用函数实现*/
// for (int i = 0; i < times; i++)
// {
// for (int j = 0; j < times - i; j++)
// {
// if (arr5[j]>arr5[j+1])
// {
// swapValues(arr5[j], arr5[j+1]);
// }
// }
// }

system("pause");

return 0;
}

二维数组

二维数组的四种定义方式

1
2
3
4
数据类型 数据名[行数][列数];
数据类型 数据名[行数][列数]={{数据1,数据2},{数据3,数据4}};
数据类型 数据名[行数][列数]={数据1,数据2,数据3,数据4};
数据类型 数据名[][列数]={数据1,数据2,数据3,数据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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <iostream>
#include <string>
#include <ctime>

using namespace std;
/*二维数组
二维数组就是在一维数组基础上个多加了一个维度
二维数组定义有四种方式
数据类型 数据名[行数][列数];
数据类型 数据名[行数][列数]={{数据1,数据2},{数据3,数据4}};
数据类型 数据名[行数][列数]={数据1,数据2,数据3,数据4};
数据类型 数据名[][列数]={数据1,数据2,数据3,数据4}*/
int main() {

/*第一种二维数组的定义方式*/
int arr[2][3];
arr[0][0] = 1;
arr[0][1] = 2;
arr[0][2] = 3;
arr[1][0] = 4;
arr[1][1] = 5;
arr[1][2] = 6;

for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++) {
cout << arr[i][j];
}cout << endl;
}

int arr2[2][3] =
{
{1,2,3},
{4,5,6}
};

for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++) {
cout << arr2[i][j];
}cout << endl;
}

int arr3[2][3] =
{1,2,3,4,5,6};
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++) {
cout << arr3[i][j];
}cout << endl;
}/*默认情况下他先按照行输入,满了之后换下一行*/

int arr4[][3] =
{ 1,2,3,4,5,6 };

for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++) {
cout << arr3[i][j];
}cout << endl;
}
system("pause");

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

using namespace std;



int main() {

int arr1[2][3] =
{
{1,2,3},
{4,5,6}
};
cout << "二维数组所占内存空间:" << sizeof(arr1) << endl;
cout << "二维数组第一行所占内存 :" << sizeof(arr1[0]) << endl;
cout << "二维数组第一个元素所占内存 :" << sizeof(arr1[0][0]) << endl;

cout << "二维数组有多少行 :" << sizeof(arr1)/ sizeof(arr1[0]) << endl;
cout << "二维数组有多少列 :" << sizeof(arr1[0]) / sizeof(arr1[0][0]) << endl;



/*可以查看二维数组首地址*/
cout << "二维数组的首地址" << (int)arr1 << endl;
cout << "二维数组第一行的首地址" << (int)arr1[0] << endl;
cout << "二维数组第二行的首地址" << (int)arr1[1] << endl;

system("pause");

return 0;
}

二维数组应用案例

在一次考试中成绩分别如下表,请分别输出三名同学的总成绩

语文 数学 英语
张三 100 100 100
李四 90 50 100
王五 60 70 80
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>
#include<string>
#include<ctime>

using namespace std;

/*二维数组应用案例
案例描述:有三名同学(张三李四王五)
在一次考试中成绩分别如下表,请分别输出三名同学的总成绩
*/

int main() {
/*自己写的*/
cout << "========自己写的==========" << endl;

int score[3][3] =
{
{100,100,100},
{90,50,100},
{60,70,80}
};

int sum = 0;
string name = "";

for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
sum += score[i][j];
}
switch (i)
{
case 0:
name = "张三";
break;
case 1:
name = "李四";
break;
case 2:
name = "王五";
break;
default:
break;
}
cout << name << "的总成绩:" << sum << endl;
sum = 0;
}

cout << "========视频写的==========" << endl;
int score2[3][3] =
{
{100,100,100},
{90,50,100},
{60,70,80}
};
string name1[3] = { "张三","李四","王五" };

for (int i = 0; i <3; i++)
{
int sum2 = 0;
for (int j = 0; j < 3; j++) {
sum2 += score2[i][j];
cout << score[i][j]<<" ";

}
//cout <<"\n" << "第" << i + 1 << "个人的总分为:" << sum2 << endl;
cout <<"\n" << name1[i] << "的总分为:" << sum2 << endl;
}

system("pause");
return 0;


}

函数

概述

作用:将一段经常使用的代码封装起来,减少重复代码
一个较大的程序,一半分为若干个程序块,每个模块时下按特定的功能

函数的定义

函数的定义一般主要有五个步骤
1、返回值类型
2、函数名
3、参数列表
4、函数体语句
5、return表达式
语法

1
2
3
4
5
返回值类型 函数名 (参数列表)
{
函数体
return语句
}

定义一个加法函数,传入两个参数,相加

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>
#include <string>
#include <ctime>

using namespace std;

/*1、返回值类型
2、函数名
3、参数列表
4、函数体语句
5、return表达式*/

/*实现一个加法函数,功能是,传入两个整型数据
计算数据相加的结果,并且返回*/

int add( int num1,int num2 )
{
int sum = num1 + num2;

return sum;
}


int main() {

int a1 = add(1, 2);
int a2 = 10;
int a3 = 20;
int a4 = add(a2, a3);
int a5 = add(a1, a2);


cout << add(1, 2) << endl;
cout << "a1:"<<a1 << endl;
cout << "a4:"<<a4 << endl;
cout << "a5:"<<a5 << endl;


system("pause");

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

using namespace std;

/*函数调用*/

int add(int num1, int num2)
{
int sum = num1 + num2;
return sum;
}
int main() {
/*函数调用语法: 函数名(参数)*/
/*a和b称为 实际参数 实际上是有值的
实参 传入的参数
再看函数在定义的时候 num1 和 num2
并没有实际的值 所以是形式参数 也就是形参*/
int c = add(a, b);

system("pause");
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
#include<iostream>
#include<string>

using namespace std;

int add(int num1, int num2)
{
int sum = num1 + num2;
return sum;
}

/*定义函数 实现两个数字进行交换*/
void swap(int n1, int n2) {

cout << "before" << endl;
cout << "n1="<< n1<< endl;
cout << "n2="<< n2<< endl;

int n3 = n1;
n1 = n2;
n2 = n3;

cout << "after" << endl;
cout << "n1=" << n1 << endl;
cout << "n2=" << n2 << endl;

//return; 返回值不需要的时候 可以不写return
}
int main() {

int a1 = 10;
int a2 = 20;
swap(a1, a2);

cout << "a1=" << a1 << endl;
cout << "a2=" << a2 << endl;
/*输出了之后发现a1 a2的值没变
说明交换只在函数内有效*/

/*用专业的语句来说 就是我们做值传递的时候
函数的形参发生改变,并不会影响实参数
现在根据我的理解,值传递就是赋值
就是调用函数的时候把实际的两个变量 传递进函数的两个变量
也就是传递进函数的两个实参的值,传递给函数内的形参
到上面为止的所有方法都是值传递
值传递的时候形参发生的任何改变都不会影响实参*/

system( "pause" );
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
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>
#include<string>

using namespace std;

/*四种常见的函数*/
/*无参无返*/
void test01()
{
cout << "this is test01" << endl;
}
/*有参无返*/
void test02(int a)
{
cout << "int a is :" << a << endl;
}
/*无参有返*/
int test03()
{
cout << "this is test03" << endl;
return 1000;
}
int test04(int a)
{
cout << "this is test04" << endl;
return a;
}

int main() {

/*无参无返的函数调用*/
test01();
/*有参无返的函数调用*/
test02(2);
/*无参有返的函数调用*/
int a1 = test03();
cout << "a1=" << a1 << endl;
/*有参有返的函数调用*/
int a2 = test04(10000);
cout << "a2=" << a2 << endl;

system("pause");
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
#include<iostream>
#include<string>

using namespace std;

/*函数的声明*/
/*比较函数,实现两个整形的数字进行比较
返回较大的值*/

/*提前告诉编译器函数存在 可以利用函数的声明*/
/*函数的声明*/
int max(int a, int b);
/*声明可以写多次,但是定义只能有一次*/
/*个人看法 就是把函数体中的部分去掉*/


int main()
{
int a1 = 10;
int a2 = 20;
cout << max(a1, a2) << endl;
}


/*定义函数*/
int max(int a, int b)
{
return a > b ? a : b;
}

函数的分文件编写

作用:让代码结构更加清晰
函数分文件编写一般有4个步骤
1、创建后缀名为.h的头文件
2、创建后缀名为.cpp的源文件
3、再头文件中写函数的声明
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
swap.h
#pragma once
#include <iostream>
using namespace std;

/*函数的声明*/
void swap(int a, int b);
swap.cpp
#include "swap.h"

/*定义*/
void swap(int a, int b)
{
int tempo = a;
a = b;
b = tempo;

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

}
test035.cpp
#include <iostream>
#include <string>
#include <ctime>
#include "swap.h"

using namespace std;
/*函数的分文件编写*/
/*实现两个数字*/

int main() {

int a1 = 10;
int a2 = 20;
swap(a1, a2);

system("pause");

return 0;
}

指针

指针的基本概念

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

内存编号是从0开始记录的,一般用十六进制表示
可以利用指针变量保存地址

image-20241220203516452

指针变量的定义和使用

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

为什么要写数据类型:定义指针时需要指定数据类型,是为了保证指针的正确行为,包括内存访问、数据解释以及指针算术操作。

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;

int main()
{
/*定义指针*/
int a = 10;
/*指针定义的语法: 数据类型 * 指针变量名;*/
int * p;
/*p代表point point 代表的就是指针的意思
指向性 指针
不同的数据类型在内存中占用不同大小
不同指针*/
/*&a 获得a的地址
后面引用传递的时候也会记载*/

/*让指针记录变量a的地址*/
p = &a;
cout << "a的地址" << &a << endl;
cout << "指针p为" << p << endl;


/*使用指针*/
/*可以通过解引用的方式来找到指针指向的内存*/
/*指针前加一个*就是解引用,找到指针指向的内存中的数据*/
*p = 1000;
cout << "a的现在为" << a << endl;
cout << "p现在为" << p << endl;
cout << "*p现在为" << *p << endl;
/*个人理解:首先我们定义了一个int变量a 他的地址可能是0x00
* 然后内存中 0x00 的位置的变量就是a 假设为0
* 然后我们定义了一个 int 指针 p
* 然后让这个指针指向 a 就是说 p = 0x00
* 然后解引用就是找到这个地址中的数据 比如说找到0x00
* 然后看一下这个位置4个字节长度的数据是0000 那就是0
*/


system("pause");
return 0;
}

解引用
*p 表示 解引用操作,也就是通过指针 p 获取它所指向的地址,并操作该地址中的数据。
然后指针的定义中

  • 功能上int *p;int * p;int* p; 是等价的,指向 int 类型的指针。
  • 空格的使用 完全是风格问题,取决于个人或团队的编码习惯。

指针所占内存空间

指针也是种数据类型,那么这种数据类型占用多少内存空间?

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

int main()
{
/*指针所占内存空间*/
int a = 10;
//int* p;
//p = &a;
/*更加简洁的写法*/
int * p = &a;

cout << "size of (int *)" << sizeof(int * ) << endl;
cout << "size of (double *)" << sizeof(double * ) << endl;
cout << "size of (p)" << sizeof(p) << endl;
/*在32位操作系统下 所有指针都占4个字节
在64位操作系统下,所有指针都占8个字节
我是64位系统,所以这里都是显示占8字节*/


system("pause");
return 0;
}

空指针和野指针

空指针:指针变量指向内存中编号为0的空间
用途:初始化指针变量
注意:空指针指向的内存是不可以访问的

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

using namespace std;

int main()
{
/*空指针*/
/*空指针用于初始化*/
int *p = NULL;


/*空指针是不可以访问 的
下面 尝试解引用,然后 就失败了*/
//*p = 100;
//0-255之间的内存编号是系统占用的,因此不可以访问


system("pause");
return 0;
}

野指针

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

using namespace std;

int main() {

/*野指针*/
/*在程序中尽量避免出现野指针
野指针就是直接赋值了一块地址
但是这块地址实际上是没有权限去操作的
因为正常可以操作的地址是通过正常定义申请得来的*/
int* p = (int*)0x1100;


system("pause");

return 0;
}

空指针和野指针都不是我们申请的空间,因此不要访问。

const修饰指针

const 修饰指针有三种情况
const修饰指针 常量指针
const修饰常量 指针常量
const既修饰指针 又修饰常量

常量指针

特点:指针的指向可以修改,但是指针指向的值不可以改。

image-20241220221523804

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>
#include <string>
#include <ctime>

using namespace std;
/*常量指针
常量指针 首先是指针, 然后是const修饰的指针
const导致这个指针变成了一个常量*/



int main() {

int a = 10;
int b = 10;
int c = 20;
/*常量指针*/
int * p1 = &a;
/*上面就是定义一个指针的正常语
然后如果要定义一个常量指针 首先他是一个指针
其次用常量修饰 所以应该用const修饰这个指针的定义*/
const int* p2 = &a;

cout << "*p2 = " << *p2 << endl;
/* 指针的指向可以修改,
但是指针指向的值不可以改
首先如果我定义一个指针
比如在这里,我能*/
p2 = &b;
cout << "*p2 = " << *p2 << endl;
/*为什么这里用p1 = &b;而不是*p1 = &b;
因为p1 = &b;的意思是把p1的指向从a改成b
而*p1 = &b;的意思是把p1指向的内容 ,也就是a指向的内存所占的空间中的数据改成b
这肯定是不行的,因为a是一个int 所以只能存储数字,b在这里只是一个字母*/

p2 = &c;
cout << "*p2 = " << *p2 << endl;
/*从这里我们可以知道 首先这个所谓的常量指针
说白了就是不可以解引用然后修改内存地址中存储的数字,
但是实际上还是可以指向不同的数的 这里可以指向代表20的c
也可以指向代表10的b 说明就是缺少了解引用的功能
我的猜测是可能可以用来防止修改已有的变量吧*/


system("pause");

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
#include<iostream>
using namespace std;

/*指针常量*/

int main()
{
int a = 10;
int b = 10;
int c = 20;

/*正常指针*/
int* p2 = &a;
/*指针常量*/
int* const p1 = &a;
cout << "p1=" << p1 << endl;
*p1 = 20;
cout << "p1=" << p1 << endl;
cout << "*p1 = " << *p1 << endl;
cout << "a = " << a << endl;
/*这里可以看到 p1的地址没有发生改变
也就是说p1一直是指向那一块内存空间的
但是p1的值*/


system("pause");
return 0;

}

const既修饰指针,又修饰常量

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>
#include <string>
#include <ctime>

using namespace std;

/*const 既修饰指针 也修饰常量*/
int main() {

int a = 10;
int b = 20;
int c = 30;

const int* const p1 = &a;
int const * const p2 = &a;
cout << " p1=" << p1 << endl;
cout << " p2=" << p2 << endl;
cout << " *p1=" << *p1 << endl;
cout << " *p2=" << *p2 << endl;

/*指针的指向和指针的值都不可以修改*/

system("pause");

return 0;
}

理解看法和总结

说一下这一块我整体的看法和总结,首先先来看指针的定义
指针定义过程中的int p = &xxx; 其中 代表他是一个指针,然后p实际上就是一个数字,用来存储内存地址
常量指针,常量作为一个形容词修饰指针,所以const 肯定在 的前面(无所谓在int前后)
所以他作为指针常量,只是指针的
部分被限制住了,在解引用的时候要用到,所以就没办法用了
说人话就是没办法用
p = 10;这种语句来修改内存地址中的数据了。这是常量指针 指针的部分被常量限制了。
然后是指针常量,指针常量在前面,首先是指针 然后才是常量 所以定义的方式是 int const p = &a;这样,然后p的部分本来存储的就是内存地址,被const修饰之后 内存地址就没办法更改了,但是 的部分功能还是正常的,也就是说我们可以解引用修改内存地址中的数据 但是没办法修改引用的内存地址了。 这 p永远都只能指向a
最后是第三种 const int * const p = &a 这种 情况下 既没办法修改地址 也没办法解引用修改值。

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 <string>
#include <ctime>

using namespace std;

/*const 既修饰指针 也修饰常量*/
int main() {

int a = 10;
int b = 20;
int c = 30;

const int* const p1 = &a;
int const * const p2 = &a;
cout << " p1=" << p1 << endl;
cout << " p2=" << p2 << endl;
cout << " *p1=" << *p1 << endl;
cout << " *p2=" << *p2 << endl;

/*指针的指向和指针的值都不可以修改*/

/*1\ const修饰指针*/
const int* p3 = &a;
p3 = &b;
cout << "p3:" << p3 << endl;
cout << "*p3:" << *p3 << endl;

/*2\ const修饰常量*/
int* const p4 = &a;
*p4 = 30;
cout << "p4"<<p4<< endl;
cout << "*p4" << *p4<< endl;
cout << "a=" << a << endl;

/*3 const修饰指针和常量*/
const int* const p5 = &a;

cout << "p5" << p5 << endl;
cout << "*p5" << *p5 << endl;


system("pause");

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

using namespace std;

int main()
{
/*指针和数组
利用指针访问数组中的元素*/

int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };

cout << "第一个元素" << arr1[0] << endl;
/*数组是整形的 所以我们创建指针的时候也要创建整形的指针*/

int* p = arr1;
/*arr就是数组首地址*/
cout << "利用指针访问第一个元素:" << *p << endl;

p++;
cout << "P++元素:" << *p << endl;
/*p++之后访问的就是数组中的第二个元素了*/

cout << "利用指针遍历数组" << endl;
int* p2 = arr1;
int length = sizeof(arr1) / sizeof(arr1[0]);

for (int i = 0; i < length; i++)
{
/*不用指针*/
cout << "第" << i << "个数是" << arr1[i] << endl;
/*用指针*/
cout << *p2 << endl;
p2++;
}


system("pause");
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
#include <iostream>
using namespace std;

/*写一个函数实现两个数字进行交换*/
void swap01(int n1, int n2)
{

cout << "交换前" << endl;
cout << "swap01函数中n1:" << n1 << endl;
cout << "swap01函数中n2:" << n2 << endl;
int tempo = n1;
n1 = n2;
n2 = tempo;

cout << "交换后" << endl;
cout << "swap01函数中n1:" << n1 << endl;
cout << "swap01函数中n2:" << n2 << endl;


}

/*值传递 要来力*/
void swap02(int *p1 , int *p2)
{
int tempo = *p1;
*p1 = *p2;
*p2 = tempo;
/* *p是解引用的意思 这里是引用了a和b
然后传入了两个内存地址的解引用
然后用解引用重新赋值*/
}


int main()
{
/*指针和函数*/
/*值传递*/
int a = 10;
int b = 20;
swap01(a, b);

cout << "main函数中a:" << a << endl;
cout << "main函数中b:" << b << endl;

/*地址传递*/
swap02(&a, &b);
cout << "地址传递后" << endl;
cout << "main函数中a:" << a << endl;
cout << "main函数中b:" << b << endl;


system("pause");
return 0;

}

指针、数组、函数

封装一个函数,利用冒泡排序,实现对整整形数组的升序排序
举例 int arr1[10]= {4,3,6,9,5,8,2,1,7,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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <iostream>
#include <string>
#include <ctime>

using namespace std;
/*参数1 数组的首地址 参数二 数组长度*/
void bubbleSort(int* p, int len)
{
for (int i = 0; i < len-1; i++)
{
for (int j = 0; j < len - 1 - i; j++)
{
if (p[j]>p[j+1])
{
int tempo = p[j];
p[j] = p[j + 1];
p[j + 1] = tempo;
}
}
}
}
/*问 为什么这里可以用p[j]访问数组元素*/
/*在 C++ 中,指针和数组是密切相关的。
实际上,数组名本质上是一个指针,指向数组的第一个元素。
arr[j] 实际上是对指针 arr 的解引用操作(即 *(arr + j))。
它等价于指针的偏移。
你可以通过 arr + j 来访问数组的第 j 个元素,
因为 arr 是一个指向数组首元素的指针,arr + j 就是指向数组第 j 个元素的指针。*/



/*写一个 打印数组的函数*/
void printArray(int* p, int len)
{
for (int i = 0; i < len; i++)
{
cout << p[i];
}
}


int main() {

int arr1[10] = { 4,3,6,9,5,8,2,1,7,5 };
int len = sizeof(arr1) / sizeof(arr1[0]);

//for (int i = 0; i < len; i++)
//{
// cout << arr1[i];
//}

printArray(arr1,len);
cout << "\n" << "排序前如上" <<"\n"<<"排序后如下" << endl;

bubbleSort(arr1, len);
//for (int i = 0; i < len; i++)
//{
// cout << arr1[i] ;
//}



printArray(arr1, len);
cout << endl;
system("pause");

return 0;
}

结构体()

结构体的基本概念

结构体属于用户自定义的数据类型,允许用户存储不同的数据类型
我的感觉就是java中自定义的类,但是好像又不一样

结构体定义和使用

语法:struct 结构体名{结构体成员列表};
通过结构体创建变量的方式有三种:
struct 结构体名 变量名
struct 结构体名 变量名 = {成员值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
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

/*创建学生数据类型
学生包括 姓名 年龄 分数*/
/*语法 struct 类型名称{成员列表}*/
struct Student
{
/*成员列表*/


/*姓名 年龄 分数*/
string name;
int age;
int score;
}s3;
/*在这里可以顺便创建一个结构体变量
但是我感觉不会有人这么写*/

/*为了偷懒我这里写了一个函数打印学生信息*/
void printStudent(Student p1)
{
cout << "name:" << p1.name << " age:" << p1.age << " score:" << p1.score << endl;
}

/*通过学生类创建具体学生*/

int main()
{
/*三种方法创建*/
//struct Student s1; struct 可以省略
Student s1;
s1.age = 18;
s1.name = "jack";
s1.score = 90;
printStudent(s1);


struct Student s2 = { "mike",20,90 };
/*这里和java又不一样 java是括号 这里是 花括号*/
printStudent(s2);

s3.age = 20;
s3.name = "fiona";
s3.score = 95;
printStudent(s3);

system("pause");

return 0;
}

总结
1、定义结构体的关键字是 struct 类似java中的class 不可省略
2、创建结构体变量的时候,关键字stcuct可以省略
3、结构体利用操作符”.”访问成员

结构体数组

作用:将自定义的结构体放入数组中方便维护
我认为可能跟用java做 管理系统需要把自定义的类放入到arraylist差不多
语法: struct 结构体名 数组名[元素个数 ] = { {},{}…{} }

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>
#include <string>
#include <ctime>

using namespace std;


struct Student
{
string name;
int age;
int score;
};

void printStudent(Student p1)
{
cout << "name:" << p1.name << " age:" << p1.age << " score:" << p1.score << endl;
}

/*结构体数组*/
/*1 定义结构体
2 创建结构体数组
3 给结构体数组中的元素赋值*/
int main() {



struct Student stuArr[3] =
{
{"mike",17,70},
{"alice",18,80},
{"john",19,90}
};
/*如果要修改*/
stuArr[2].name = "jack";

/*尝试一下遍历输出*/
int len = sizeof(stuArr) / sizeof(stuArr[0]);
for (int i = 0; i < len; i++)
{
printStudent(stuArr[i]);
}/*成功*/


system("pause");

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
#include <iostream>
#include <string>
#include <ctime>
using namespace std;

struct Student
{
string name;
int age;
int score;
};

void printStudent(Student *p1)
{
cout << "name:" << p1->name << " age:" << p1->age << " score:" << p1->score << endl;
}


int main() {

/*创建学生结构体 变量
通过指针指向结构体变量
通过指针访问结构体变量中的数据*/

Student s1 = { "jack",18,90 };
Student * p = &s1;

printStudent(p);


system("pause");

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

using namespace std;

struct Student
{
string name;
int age;
int score;
};

struct Teacher
{
int id;
string name;
int age;
Student stu;
};


void printTeacherInfo(const Teacher& t)
{
// 输出教师信息
cout << "Teacher ID: " << t.id << "Teacher Name: "
<< t.name<< "Teacher Age: " << t.age << endl;

// 输出学生信息
cout << "Student Name: " << t.stu.name
<< "Student Age: " << t.stu.age
<< "Student Score: " << t.stu.score << endl;
}


int main() {

Student s1 = {"jack",18,70};
Student s2 = {"mike",19,80};
Student s3 = {"lisa",20,90};

Teacher t1 = {1,"white",40,s1};
Teacher t2 = {2,"greffin",46,s2};
Teacher t3 = {3,"park",38,s3};

printTeacherInfo(t1);
printTeacherInfo(t2);
printTeacherInfo(t3);


system("pause");

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

using namespace std;

/*学生类*/
struct Student
{
string name;
int age;
int score;
};

/*打印学生 信息的函数*/
/*值传递*/
void printStudent01(Student s1)
{
cout << "值传递函数内" << endl;
s1.age = 100;
cout << "name:" << s1.name << " age:" << s1.age << " score:" << s1.score << endl;
}

/*地址传递 引用传递*/
void printStudent02(Student *p)
{
cout<<"引用传递函数内" << endl;
p->age = 100;
cout << "name:" << p->name << " age:" << p->age << " score:" << p->score << endl;
}

int main() {

Student s1 = { "jack",18,70 };
Student s2 = { "mike",19,80 };
Student s3 = { "alice",20,90 };
cout << "name:" << s1.name << " age:" << s1.age << " score:" << s1.score << endl;
printStudent01(s1);
cout << "值传递修改后" << endl;
cout << "name:" << s1.name << " age:" << s1.age << " score:" << s1.score << endl;

printStudent02(&s1);
cout << "地址传递修改后" << endl;
cout << "name:" << s1.name << " age:" << s1.age << " score:" << s1.score << endl;

system("pause");

return 0;
}

结构体中const使用场景

作用:用const防止误操作

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<string>

using namespace std;

struct Student
{
string name;
int age;
int score;
};

void printStudent(Student s)
{
cout << "name:" << s.name << " age:" << s.age << " score:" << s.score << endl;
}/*这就是标准的值传递 值传递会拷贝出非常多的多余数据 */

/*尝试用引用 传递*/
/*为什么用指针 因为上面的代码相当会把Student中
所有的数据都复制出来一份,在实际情况中,student的人数会很多
student的信息也会很多 ,数据量就会按照指数级增长
所以用指针可以很好的解决这个问题,一个指针只占4个字节(64位系统是八个)*/
/*一句话概括 用指针 可以减少内存空间 同时不会复制新的副本*/


void printStudent02(const Student* s)

{/*这节课的重点 就是上面在函数定义的时候在形参前面加一个const 这样的话 就无法修改
引用的指针,就是所谓的 常量指针 可以修改引用不同的指针 传入不同的student类的数据
都可以读数据 但是都无法更改 student中的内容*/
//s->name = "mike" 加入const之后这里就会报错 不能进行修改
/*这样就可以保证不修改原始数据了*/
cout << "name:" << s->name << " age:" << s->age << " score:" << s->score << endl;
}

int main()
{
Student s1 = { "jack",17,70 };
printStudent(s1);

printStudent02(&s1);



system("pause");
return 0;
}

结构体案例

案例1

案例描述:
学校正在做毕设项目,每名老师带领5个学生,总共有3名老师,
需求如下
设计学生和老师的结构体,其中在老师的结构体中,有老师姓名和一个存放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>
#include<string>
#include<ctime>

using namespace std;

struct Student
{
string sname;
int score;
};
struct Teacher
{
string tname;
Student stuArr[5];
};

/*给老师和学生赋值的函数*/
void allocatSpace(Teacher tArr[], int len)
{
string nameSeed = "ABCDE";
for (int i = 0; i < len; i++)
{
/*给老师的姓名赋值*/
tArr[i].tname = "Teacher_";
tArr[i].tname += nameSeed[i];
/*给老师的学生赋值 通过循环*/
for (int j = 0; j < 5; j++)
{
tArr[i].stuArr[j].sname = "student_";
tArr[i].stuArr[j].sname += nameSeed[j];


/*给成绩赋值*/
int random = rand() % 61 + 40;
tArr[i].stuArr[j].score = random;
}
}
}

void printInfo(Teacher tArr[],int len)
{
for (int i = 0; i < len; i++)
{
cout << "teacher name:" << tArr[i].tname << endl;
for (int j = 0; j < 5; j++)
{/* 打印学生姓名和成绩*/
cout << "\t student name:" << tArr[i].stuArr[j].sname
<< " score:" << tArr[i].stuArr[j].score << endl;
}

}
}

int main()
{
/*随机数种子*/
srand((unsigned int)time(NULL));
/*创建三名老师的数组*/
Teacher tArr[3];

/*通过函数赋值*/
int len = sizeof(tArr) / sizeof(tArr[0]);
allocatSpace(tArr, len);

/*打印信息*/
printInfo(tArr, len);



system("pause");
return 0;

}

案例2

设计一个英雄的结构体,包括成员姓名,年龄,性别;创建结构体数组,数组中存放5名英雄。
通过冒泡排序的算法,将数组中的英雄按照年龄进行升序排序,最终打印排序后的结果。

五名英雄信息如下

1
2
3
4
5
{"刘备",23,"男"},
{"关羽",22,"男"},
{"张飞",20,"男"},
{"赵云",21,"男"},
{"貂蝉",19,"女"}
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
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

/*英雄结构体*/
struct hero
{
string name;
int age;
string gender;
};

/*排序算法*/
void bubler(hero *p,int len)
{
for (int i = 0; i < len-1; i++)
{
for (int j = 0; j < len -1 - i; j++)
{
if (p[j].age > p[j + 1].age)
{
hero tempo = p[j];
p[j] = p[j + 1];
p[j + 1] = tempo;
}
}
}
}

void show(hero* p, int len)
{
for (int i = 0; i < len; i++)
{
cout << "name:" << p[i].name << " age:" << p[i].age << " gender:" << p[i].gender << endl;
}
}

int main() {

hero HeroList[5] =
{
{"刘备",23,"男"},
{"关羽",22,"男"},
{"张飞",20,"男"},
{"赵云",21,"男"},
{"貂蝉",19,"女"}
};

int len = sizeof(HeroList) / sizeof(HeroList[0]);

bubler(HeroList, len);

/*输出*/
show(HeroList, len);

system("pause");

return 0;
}

第一大项目

尝试做通讯录管理系统

系统需求

通讯录是一个可以记录亲人、好友信息的工具。
本教程主要利用C++来实现一个通讯录管理系统系统中需要实现的功能如下:
添加联系人:向通讯录中添加新人,信息包括(姓名、性别、年龄、联系电话、家庭住址)最多记录1000人
显示联系人:显示通讯录中所有联系人信息
删除联系人:按照姓名进行删除指定联系人
查找联系人:按照姓名查看指定联系人信息
修改联系人:按照姓名重新修改指定联系人
清空联系人:清空通讯录中所有信息
退出通讯录:退出当前使用的通讯录

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
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
#include<iostream>
#include<string>
#include<ctime>
#define max 1000

using namespace std;
/*封装函数显示该界面 如void showMenu()
在main 函数中调用封装好的函数 */

/*联系人结构体*/
struct Person
{
string m_name;
int m_sex;
int m_age;
string m_phone;
string m_addr;
};

struct AddressBooks
{
struct Person PersonArray[max];
int m_size;
};



/*菜单界面*/
void showMenu()
{
cout << "*************************" << endl;
cout << "***** 1. 添加联系人 *****" << endl;
cout << "***** 2. 显示联系人 *****" << endl;
cout << "***** 3. 删除联系人 *****" << endl;
cout << "***** 4. 查找联系人 *****" << endl;
cout << "***** 5. 修改联系人 *****" << endl;
cout << "***** 6. 清空联系人 *****" << endl;
cout << "***** 0. 退出通讯录 *****" << endl;
cout << "*************************" << endl;
}

/*添加联系人*/
void addPerson(AddressBooks * abs)
{
/*判断通讯录是否已满,如果满了就不再添加*/
if (abs->m_size ==max)
{
cout << "通讯录已满,无法继续添加!" << endl;
return;
}
else
{
string name;
/*添加具体联系人*/
cout << "请输入姓名" << endl;
cin >> name;
abs->PersonArray[abs->m_size].m_name = name;

int gender = 0;
cout << "请输入性别" << endl;
cout << "1--男,2--女" << endl;


while (true)
{/*如果输入的是一或者2 可以退出循环
因为输入的是正确值 如果输入有误,重新输入*/
cin >> gender;
if (gender == 1 || gender == 2)
{
abs->PersonArray[abs->m_size].m_sex = gender;
break;
}
cout << "输入有误,请重新输入" << endl;
}

/*年龄*/
cout << "请输入年龄:" << endl;
int age = 0;
cin >> age;
abs->PersonArray[abs->m_size].m_age = age;

cout << "请输入联系电话:" << endl;
string phone = "";
cin >> phone;
abs->PersonArray[abs->m_size].m_phone = phone;

cout << "请输入住址:" << endl;
string address = "";
cin >> address;
abs->PersonArray[abs->m_size].m_addr = address;

/*更新通讯录人数*/
abs->m_size++;
cout << "添加成功" << endl;

system("pause")/*请按任意键继续*/;
system("cls");/*清屏*/

}

}


/*显示联系人*/
void showPerson(AddressBooks* abs)
{
/*判断通讯录中的人数*/
/*如果不为0 显示记录的联系人的信息*/
if (abs->m_size == 0)
{
cout << "当前记录为空" << endl;

}
else
{
for (int i = 0; i < abs->m_size; i++)
{
cout << "姓名:" << abs->PersonArray[i].m_name << endl;
cout << "性别:" << (abs->PersonArray[i].m_sex==1?"男":"女") << endl;
cout << "年龄:" << abs->PersonArray[i].m_age << endl;
cout << "电话:" << abs->PersonArray[i].m_phone << endl;
cout << "住址:" << abs->PersonArray[i].m_addr << endl;

}
}
system("pause")/*请按任意键继续*/;
system("cls");/*清屏*/

}


/*删除功能 */
int isExist(AddressBooks* abs, string name)
{
for (int i = 0; i < abs->m_size; i++)
{
if (abs->PersonArray[i].m_name == name)
{
/*找到用户*/
return i;
}
}
return -1;
}

/*删除指定的联系人*/
void deletePerson(AddressBooks *abs)
{
cout << "请输入你要删除的联系人" << endl;
string name;
cin >> name;

int ret = isExist(abs, name);
if (ret != -1)
{
/*查找到这个人 删除
要删除 一个数据 将后面的数据向前移动
并且让通讯录中记录的人员数量做-1的操作即可*/
for (int i = ret; i < abs->m_size; i++)
{
/*数据前移*/
abs->PersonArray[i] = abs->PersonArray[i + 1];

}
abs->m_size--;/*更新一下通讯录中的人员数*/
cout << "delete complete!" << endl;
}else
{
cout << "404 not found" << endl;
}
system("pause")/*请按任意键继续*/;
system("cls");/*清屏*/
}

/*查找联系人*/
void findPerson(AddressBooks * abs)
{
cout << "请输入您要查找的联系人" << endl;
string name;
cin >> name;

/*判断指定的姓名是否存在 */
int ret = isExist(abs, name);

if (ret != -1)
{
cout << "姓名:" << abs->PersonArray[ret].m_name << endl;
cout << "性别:" << (abs->PersonArray[ret].m_sex == 1 ? "男" : "女") << endl;
cout << "年龄:" << abs->PersonArray[ret].m_age << endl;
cout << "电话:" << abs->PersonArray[ret].m_phone << endl;
cout << "住址:" << abs->PersonArray[ret].m_addr << endl;
}
else
{
cout << "查无此人" << endl;
}
system("pause")/*请按任意键继续*/;
system("cls");/*清屏*/
}


/*修改联系人*/
void modifyPerson(AddressBooks *abs)
{
cout << "请输入您要修改的联系人" << endl;
string name;
cin >> name;
/*判断指定的姓名是否存在 */
int ret = isExist(abs, name);
if (ret != -1)
{
string name;
/*添加具体联系人*/
cout << "请输入姓名" << endl;
cin >> name;
abs->PersonArray[ret].m_name = name;

int gender = 0;
cout << "请输入性别" << endl;
cout << "1--男,2--女" << endl;
while (true)
{/*如果输入的是一或者2 可以退出循环
因为输入的是正确值 如果输入有误,重新输入*/
cin >> gender;
if (gender == 1 || gender == 2)
{
abs->PersonArray[ret].m_sex = gender;
break;
}
cout << "输入有误,请重新输入" << endl;
}

/*年龄*/
cout << "请输入年龄:" << endl;
int age = 0;
cin >> age;
abs->PersonArray[ret].m_age = age;

cout << "请输入联系电话:" << endl;
string phone = "";
cin >> phone;
abs->PersonArray[ret].m_phone = phone;

cout << "请输入住址:" << endl;
string address = "";
cin >> address;
abs->PersonArray[ret].m_addr = address;
cout << "修改成功" << endl;

}
else
{
cout << "查无此人" << endl;
}
system("pause")/*请按任意键继续*/;
system("cls");/*清屏*/

}
/*清空联系人*/
void claenPerson(AddressBooks* abs)
{
abs->m_size = 0;
cout << "已清空" << endl;
system("pause")/*请按任意键继续*/;
system("cls");/*清屏*/
}

int main()
{
/*创建通讯录结构体变量*/
AddressBooks abs;
/*初始化通讯录中当前人员个数*/
abs.m_size = 0;

while(true)
{
showMenu();

/*创建用户选择输入的变量*/
int select = 0;
cin >> select;

switch (select)
{

case 1:
/*添加联系人*/
addPerson(&abs);
/*利用地址传递可以修饰实参*/

break;
case 2:
/*显示联系人*/
showPerson(&abs);
break;
case 3:
//{/*删除联系人*/
// cout << "请输入删除联系人的姓名" << endl;
// string name = "";
// cin >> name;
// if (isExist(&abs, name) == -1)
// {
// cout << "404 not found" << endl;
// }
// else
// {
// cout << "got you" << endl;
// }
//}
deletePerson(&abs);
break;
case 4:
/*查找联系人*/
findPerson(&abs);
break;
case 5:
/*修改联系人*/
modifyPerson(&abs);
break;
case 6:
/*清空联系人*/
claenPerson(&abs);
break;
case 0:
/*退出通讯录*/
cout << "byebye~" << endl;
system("pause");
return 0;
break;
default:
break;
}
}


system("pause");
return 0;
}

中途还是有很多问题 但是至此 通讯录管理系统完成

▲C++核心编程

主要是面向对象 技术的详解

内存的分区模型

C++程序在执行时,将内存大方向划分为四个区域
代码区:存放函数体的二进制代码,由操作系统进行管理(cout ,字符)
全局区:存放全局变量和静态变量以及常量
栈区:由编译器自动分配释放,存放函数的参数值,局部变量等
堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收

内存四区的意义:不同区域存放 的数据,赋予不同的生命周期,给我们更大的灵活编程。

程序运行前

在程序编译后,生成了exe可执行程序,未执行该程序前分为两个区域
代码区
存放cpu执行的机器指令
代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在 内存中有一份 代码即可
代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令
全局区
全局变量和静态变量存放于此
全局区还包含了常量区,字符串常量和其他常量也存放在此
该区域的数据在程序结束后由操作系统释放。

程序运行后

栈区
由编译器自动分配释放,存放函数的参数值,局部变量等
注意事项:不要反悔局部变量的地址,栈区开辟的数据由于编译器自动释放
堆区
由于程序员分配释放,若程序员不释放,程序结束时由操作系统回收
在C++中主要利用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
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
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

/*全局变量*/
int g_a = 10;
int g_b = 10;

/*const修饰的全局变量*/
const int cga = 10;
const int cgb = 10;


int * func(int b)/*返回一个int类型的指针*/
{/*形参数据也会放在栈区*/
int a = 10;/*存放在栈区 栈区的数据在函数执行完之后自动释放*/
return &a;
/*这里就是在返回局部变量的地址*/
}

int * func1()
{/*利用new关键字 可以将数据开辟 到 堆区*/

/*指针本质上给是一个局部变量 也是放在栈上
指针保存的数据 放在堆区*/
int * p = new int(10);
return p;

}

int main() {

/*全局区*/

/*全局变量 静态变量 常量*/


/*创建一个普通的局部变量*/
int a = 10;
int b = 10;
/*只要写在函数体内的变量
都叫 局部变量*/
cout << "局部变量a的地址:" << (int)&a << endl;
cout << "局部变量b的地址:" << (int)&b << endl;

cout << "全局变量g_a的地址:" << (int)&g_a << endl;
cout << "全局变量g_b的地址:" << (int)&g_b << endl;

/*静态变量*/
static int s_a = 10;
static int s_b = 10;

cout << "静态变量s_a的地址:" << (int)&s_a << endl;
cout << "静态变量s_b的地址:" << (int)&s_b << endl;

/*常量 字符串常量和const 修饰的变量*/
cout << "字符串常量的地址" << (int)&"hello world" << endl;
/*const 修饰的全局变量和局部变量*/
cout << "全局常量cga的地址:" << (int)&cga << endl;
cout << "全局常量cgb的地址:" << (int)&cgb << endl;

const int cla = 10;
const int clb = 10;
cout << "局部常量cla的地址:" << (int)&cla << endl;
cout << "局部常量clb的地址:" << (int)&clb << endl;
/*局部在代码区域 全局在全局区*/


/*栈区数据的注意事项
不要返回局部变量的地址
栈区的数据由编译器管理开辟和释放*/

//int* p1 = func();
///*接受func函数的返回值*/

//cout << *p1 << endl;/*第一次可以 打印正确的数字是因为编译器做了保留*/
//cout << *p1 << endl;/*这里理论上应该输出乱码*/

/*在堆区开辟数据*/
int* p = func1();
cout << *p << endl;
cout << *p << endl;


system("pause");

return 0;
}

new操作符(重点)

C++中利用new操作符在堆区开辟数据
堆区开辟的数据,由程序员手动开辟,手动释放 ,释放利用操作符delete
语法:new 数据类型
利用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
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

/*new基本语法*/
int* func()
{
/*在堆区创建整形数据*/
/*new返回的是一个该数据类型的指针*/
int * p =new int(10);
return p;
}

void test01()
{
int* p = func();
cout << *p << endl;
cout << *p << endl;
cout << *p << endl;

/*堆区的数据有程序员管理
如果想释放 用delete*/
delete p;
//cout << *p << endl;报错
/*内存已经被释放 再次访问就是非法操作*/
}

void test02()
{
/*创建10整形数据的数组,在堆区*/
int * arr = new int[10];
/*10代表数组元素个数*/

for (int i = 0; i < 10; i++)
{
arr[i] = i + 100;
}
for (int i = 0; i < 10; i++)
{
cout << arr[i] << endl;
}
}

int main() {
test01();
test02();
system("pause");

return 0;
}

引用

引用的基本使用

作用:给变量起别名
语法: 数据类型 & 别名 = 原名

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

using namespace std;

int main() {

/*引用基本语法*/
/*数据类型 &别名 = 原名*/
int a = 10;
int& b = a;
/*这样这块地址又叫a 又叫b*/
b = 20;
cout << a << endl;
/*a 也变成了20*/


system("pause");

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

using namespace std;

int main() {

int a = 10;
int c = 20;
//int& b;报错
int& b = a;
/*初始化之后不可以赋值*/
b = c;

cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c <<endl;
/*这里a原本是数据丢失*/
system("pause");

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>
#include <string>
#include <ctime>

using namespace std;

/*交换函数
值传递 地址传递 引用传递*/
void myswap01(int a, int b)
{
int tempo = a;
a = b;
b = tempo;
}

/*地址传递*/
void myswap02(int * a,int * b)
{
int tempo = *a;
*a = *b;
*b = tempo;

}
/*引用传递*/
void mySwap03(int& a, int& b)
{
int tempo = a;
a = b;
b = tempo;
}

int main() {

int a = 10;
int b = 20;

myswap01(a, b);

cout << "a after myswap01:" << a << endl;
cout << "b after myswap01:" << b << endl;

myswap02(&a, &b);
cout << "a after myswap02:" << a << endl;
cout << "b after myswap02:" << b << endl;

a = 10; b = 20;

mySwap03(a, b);
cout << "a after myswap03:" << a << endl;
cout << "b after myswap03:" << b << endl;


system("pause");

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

using namespace std;

/*引用作函数的返回值
不要返回局部变量的引用 */
int & test01()
{
int a = 10;
/*局部变量存放在内存四区中的栈区
调用函数结束之后就释放内存了*/
return a;
}

/*函数的调用可以作为左值存在*/
int& test02()
{
static int a = 10;
/*静态变量 存放在全局区*/
return a;
}



int main() {

int & ref = test01();

cout << "ref =" << ref << endl;
/* 变成了空的内存地址*/

int& ref2 = test02();
cout << "ref2 =" << ref2 << endl;
/*正常输出*/

test02() = 1000;
cout << "ref2 =" << ref2 << endl;

system("pause");

return 0;
}

▲引用的本质

本质:引用的本质在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
31
32
33
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

/*发现是引用 转换未int * const ref = &a */
void func(int& ref)
{
ref = 100;
}
int main() {

int a = 10;

/*自动转换int * const ref = &a ;
本质上是一个个指针常量,也说明了为什么引用不可更改 */
int& ref = a;
ref = 20;
/*颞部发现ref是引用 会自动帮我们转换成*ref = 20
* 说人话就是会自动解引用
所有为什么说引用简单 因为涉及到了大量的自动转换
在前面自己写代码的过程中我就 隐隐约约感觉到了
*/
cout << "a:" << a << endl;
cout << "ref:" << ref << endl;

func(a);

system("pause");

return 0;
}

结论:c++推荐用引用技术,因为语法方便,引用本质是指针常量,但是所有的指针操作编译器都帮我们做了
后记:妈的,那老子学那么久的指针是为了什么,牛魔酬宾,把我的时间还给我!

常量引用

作用:常量引用主要用来修饰形参,防止误操作
在函数形象 列表中,可以加const修饰形参,防止形参改变实参

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

using namespace std;

void showValue(const int &val)
{/*用const修饰之后变成只读 下面就不能再修改*/
//val = 1000;
cout << "val=" << val << endl;
}

int main() {
/*常量引用*/
/*使用场景,用来修饰形式参 防止误操作*/
int a = 10;
int& ref = a;
//int& ref1 = 10;
/*必须引用合法内存空间
10不具有合法内存空间*/
const int& ref1 = 10;
/*这样就正确了
加上const之后 编译器将代码修改
int temp = 10;
const int & ref = temp
相当于把代码改成这样的形式了*/

int a1 = 100;
showValue(a1);
cout << "a1=" << a1 << endl;

system("pause");

return 0;
}

函数提高(重要)

函数默认参数(重要)

在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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

/*函数默认参数*/
int func(int a, int b = 20, int c=30)
{
return a + b + c;
}
/*如果自己传入的数据 就优先用传入的
默认数据在没有传入数据的时候才会不得已被调用*/

/*下面这个是重要考点*/
/*1、如果某个位置已经有了默认参数,
那么这个位置往后,从左往右都必须有默认值 */
//int func2(int a,int b = 10,int c,int d )
//{/*如果这里b有参数 那么后面的c d 都必须有参数
// 不然就报错*/
// return a + b + c;
//}

/*2、如果函数声明有默认参数,函数实现就不能有默认参数
声明和定义只能有一个有默认参数*/
int func3(int a = 11, int b =10);

int func3(int a =10, int b = 20)
{/*这里有可能出现如上的情况 不知道
两边的默认函数不同 不知道按哪一边的默认参数来
所以只能有一边有默认参数
只要删除一个地方都能正常运行*/
return a + b;
}
/*报错 重定义默认参数 */


int main() {


cout << func(10) << endl;
cout << func(10, 30, 30) << endl;

/*cout << func3(10) << endl;
调用时报错*/

system("pause");

return 0;
}

函数占位参数

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

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

现阶段函数的占位参数意义不大,但是据说后面会用到。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <string>
#include <ctime>
/*函数 占位参数*/
using namespace std;

/*目前阶段占位参数 还用不到 后面课程可能会用到*/
/*占位参数还可以有默认参数*/
void func(int a, int=10)
{
cout << "this is func" << endl;
}

int main() {

func(10);

system("pause");

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
#include <iostream>
#include <string>
#include <ctime>
/*函数重载*/
using namespace std;

/*函数 重载 的满足条件
1、同一个作用域下
2、函数名称相同
3、函数的参数类型个数或者顺序不同*/
void func01()
{
cout << "func的调用" << endl;
}

void func01(int a )
{
cout << "func(int a )的调用" << endl;
}

void func01(double a)
{
cout << "func(double a )的调用" << endl;
}

void func01(int a,double b)
{
cout << "func(int,double)的调用" << endl;
}

void func01(double b, int a)
{
cout << "func(double,int)的调用" << endl;
}
/*注意事项
函数的返回值不可以作为重载的条件*/
//int func01()
//{ 这种情况就是错误的
// cout << "func的调用" << endl;
//}

int main() {

func01();
func01(1);
func01(1.0);
func01(1,1.0);
func01(1.0,1);

system("pause");

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>
#include <string>
#include <ctime>

using namespace std;
/*函数重载的注意事项*/

/*引用作为重载的条件*/
void func01(int a,int b)
{
cout << "func01(int a ,int b)" << endl;
}
void func01(int &a)
{ /*int & a = 10 不合法的*/
cout << "func01(int &a)" << endl;
}
void func01(const int &a)
{/*const int &a = 10*/
cout << "func01(const int a)" << endl;
}

/*函数重载碰到默认参数*/
void func2(int a)
{
cout << "func2(int a)" << endl;
}
void func2(int a,int b)
{
cout << "func2(int a,int b)" << endl;
}
//void func2(int a=10)
//{
// cout << "func2(int a=10)" << endl;
//}

int main() {

int a = 1;
func01(a);//调用的是int &a
func01(10);
/*上面调用的是int &a
下面调用的是const*/
system("pause");

/* a默认情况下是一个变量,
所以我用func01(a);的时候默认的情况下他选择了
可读可写的int &a版本
而10本身就作为一个常量 是不可以被更改的
所以默认他就选择了void func01(const int &a)*/

func2(a);
//func2();
/*当函数重载遇到默认参数 会出现二义性
尽量避免这种情况
如果要重载就不要写默认参数*/
return 0;
}

类和对象

C++面向对象的三大特性为:封装、继承、多态
C++认为完事万物皆对象,对下昂 上有其属性和行为

例如:
人可以作为对象,属性有姓名、年龄、身高、体重…行为有走、跑、跳、吃饭、唱歌
车也可以作为对象,属性有轮胎、方向盘、车灯.,行为有载人、放音乐、放空调…
具有相同性质的对象,我们可以抽象称为类,人属于人类,车属于车类,都可以作为地球上的东西一类

封装

封装的意义(重点)

封装是C++面向对象三大特性之一
封装的意义:
将属性和行为作为一个个整体,表现生活中的事物
讲过属性和行为加以权限控制

封装的意义一

​ 在设计类的时候,属性和行为写在一起,表现事物
语法

1
class 类名 {访问权限:属性/行为};

示例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
#include <iostream>
#include <string>
#include <ctime>
using namespace std;

const double pi = 3.14;

/*设计一个圆类 求圆周长
袁求周长的公式 2*PI * 半径*/
class Circle
{
public:/*公共权限*/
/*属性 半径*/
int r;

/*行为 获取圆的周长*/
double length()
{
return 2 * pi * r;
}
/*r是类的成员变量
对于每一个circle 都有一个r
length()是成员函数
因为 r 是类的 成员,
对于类的每个实例(对象)来说,
r 是属于该实例的
在成员函数中,你可以直接访问类的 成员变量,不需要额外传参。
所以这里不需要传参就可以直接访问*/

};



int main() {

/*通过圆类床关键具体的圆*/
Circle c1;
/*给圆对象的属性进行赋值*/
c1.r = 10;

cout << "圆的周长为:" << c1.length() << endl;


system("pause");

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

/*学生类*/
class Student
{
public:/*控制权限写在类里面*/

/*类中的属性和行为,我们统一称为成员
属性 成员属性 成员变量
行为 成员函数 成员方法*/

string name;
int id;


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

void setName(string sname)
{
name = sname;
}

void setId(int nid)
{
id = nid;
}


};/*class 后面要加分号*/



int main() {

Student s1;
s1.name = "jack";
s1.id = 1;

Student s2;
s2.name = "fiona";
s2.id = 2;

s1.show();
s2.show();

s1.setName("mike");
s1.show();

s2.setId(3);
s2.show();

system("pause");

return 0;
}
封装的意义二(重点):

类在设计时,可以把属性和行为放在不同的权限下,加以控制

访问权限有三种:
1、public 公共权限
2、protected 保护权限
3、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
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

/*访问权限
三种
公共权限 public 成员类内外都可以访问
保护权限 protected 类内可以访问 子类可以访问
私有权限 private 类内可以访问 子类不能访问

保护和私有的关系主要在继承
父类的保护权限子类可以访问
私有权限就不可以*/

class Person
{
public:
string pname;

protected:
string pcar;
private:
string password;

public:
void setFunc()
{
pname = "jack";
pcar = "BMW";
password = "123321";
}
/*这里在类内访问都是正常的,没有问题
*/
};

int main() {
/*实例化一个具体的对象进行操作*/
Person p1;

p1.pname = "mike";
/*
p1.pcar = "benz";
p1.password = "123123";
报错 说明类外不可以访问
protected和private权限
*/


system("pause");

return 0;
}

struct和class区别

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

区别
struct默认权限为公共
class 默认权限为私有

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 <string>
#include <ctime>

using namespace std;

class C1
{
int a1;
/*默认权限是私有*/
};

struct C2
{
int a2;
/*默认是公共权限*/
};

int main() {

/*struct和class 区别
struct 默认权限是公共
class 默认权限是私有*/
C1 obj1;
C2 obj2;

obj1.a1 = 1;
/*这里不可以访问
因为对于class来说
a1默认是私有的*/
obj2.a2 = 2;
/*这里可以访问
因为对于struct来说
a2默认是公共的*/


system("pause");

return 0;
}

成员属性设置为私有

优点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
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

/*成员属性设置私有
可以自己控制读写权限
对于写权限可以检测数据有效性
*/

class Person
{

private:
string pname;//姓名 可读写
int page = 18;//年龄 只读
string idol;//偶像 只写

public:
//设置姓名
void setName(string name)
{
pname = name;
}
//获取姓名
string getName()
{
return pname;
}

/*获取年龄*/
int getAge()
{
return page;
}
/*设置偶像*/
void setIdol(string name)
{
idol = name;
}

/*添加设置年龄的公共函数
然后添加一个验证
*/
void setAge(int a)
{
if (a<0 || a>150)
{
cout << "不合法字符" << endl;
return;
}
else
{
page = a;
}


}

};

int main() {

Person p1;
/*设置姓名*/
// p.pname = "jack";
//访问不到 报错
//cout << "name:" << p1.pname << endl;
//上面的语句同样报错
p1.setName("john");
cout << p1.getName() << endl;

/*对于年龄
我们只写了获取的方法
所有理论上没有任何办法可以修改年龄
p1.setAge(20);
*/
p1.getAge();
cout << p1.getAge() << endl;

p1.setIdol("jackson");
/*用get会报错 这里就不演示了*/

/*一句话形容
就是通过公共的set方法来写
通过get方法来读
通过公共的 set和get方法 我们自己就能控制读和写*/

/*添加验证 可以写 但是年龄必须在0-150之间*/
p1.setAge(160);
cout << p1.getAge() << endl;
p1.setAge(70);
cout << p1.getAge() << endl;

system("pause");

return 0;
}

练习案例:

设计立方体类

设计立方体类(Cube)
求出立方体的面积和体积
分别用全局函数和成员函数判断两个立方体是否相等

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

using namespace std;

/*立方体类设计
1、创建立方体类
2、设计属性和行为
3、获取立方体的面积和体积
4、分别利用全局函数和成员函数
判断两个立方体是否相等*/


class Cube
{
private:
/*定义私有属性长宽高*/
int length;
int wideth;
int height;
/*行为*/
public:
/*这里放getset*/
int getLen()
{
return length;
}
void setLen(int a)
{
length = a;
}

int getwide()
{
return wideth;
}
void setwide(int a)
{
wideth = a;
}
int getHeig()
{
return height;
}
void setHeig(int a)
{
height = a;
}

/*计算面积*/
int SurF()
{
int surface = (length * height + length * wideth + height * wideth) * 2;
return surface;
}

/*计算体积*/

int volume()
{
int volume = length * height *wideth;
return volume;
}

/*利用成员函数判断两个立方体是否相等*/
bool JudgeInClass(Cube& a)
{
if (height == a.getHeig() && wideth == a.getwide() && length == a.getLen())
{
return true;
}
else
{
return false;
}
}

};

/*利用全局函数判断两个立方体是否相等*/
bool Judge(Cube& c1, Cube& c2)
{
if (c1.getHeig() == c2.getHeig() && c1.getwide() == c2.getwide() && c1.getLen() == c2.getLen())
{
return true;
}
else
{
return false;
}
}



int main() {

Cube c1;
c1.setLen(7);
c1.setwide(8);
c1.setHeig(9);
cout << "c1 s surface is: " << c1.SurF() << endl;
cout << "c1 s volume is: " << c1.volume() << endl;

Cube c2;
c2.setLen(7);
c2.setwide(8);
c2.setHeig(9);
cout << "c2 s surface is: " << c2.SurF() << endl;
cout << "c2 s volume is: " << c2.volume() << endl;

/*利用全局函数判断*/
bool ret = Judge(c1, c2);
if (ret)
{
cout << "c1 and c2 is same" << endl;
}
else
{
cout << "c1 and c2 is not same" << endl;
}
/*利用成员函数判断*/
bool ret1 = c1.JudgeInClass(c2);
if (ret1)
{
cout << "c1 and c2 is same" << endl;
}
else
{
cout << "c1 and c2 is not same" << endl;
}



system("pause");

return 0;
}

点和圆的关系

设计一个圆形类(Circle)和一个点类(Point)计算点和圆的关系

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

using namespace std;

/*点和圆的关系案例
* 点*/
class Point
{
private:
int p_x;
int p_y;
public:
/*xy坐标的get and set*/
void setX(int x)
{
p_x = x;
}
void setY(int y)
{
p_y = y;
}
int getX()
{
return p_x;
}
int getY()
{
return p_y;
}

};


/*圆类*/
class Circle
{
private:
int c_r;
Point c_Center;
/*在类中可以让给另一个类作为本类的成员*/

public:
/*设置和获取半径*/
void setR(int a)
{
c_r = a;
}
int getR()
{
return c_r;
}
/*设置和获取圆心*/
void setCenter(Point center)
{
c_Center = center;
}
Point getCenter()
{
return c_Center;
}

};


void isInCircle(Circle& c, Point& p)
{
/*计算两点之间距离的平方*/
int distance =
(c.getCenter().getX() - p.getX()) * (c.getCenter().getX() - p.getX()) +
(c.getCenter().getY() - p.getY()) * (c.getCenter().getY() - p.getY());

/*计算半径的平方*/
int rad2 = c.getR()* c.getR();

/*判断*/
if (distance > rad2)
{
cout << "point is out of circle" << endl;
}
else if (distance == rad2)
{
cout << "point is on the circle" << endl;
}
else
{
cout << "point is in the circle" << endl;
}



}

int main() {

/*创建圆*/
Circle c1;
c1.setR(10);
Point center;
center.setX(10);
center.setY(10);

c1.setCenter(center);



/*创建点*/
Point p1;
p1.setX(10);
p1.setY(20);

/*判断 */
isInCircle(c1, p1);



system("pause");

return 0;
}

对象的初始化和清理(重点)

生活中我们卖的电子产品都有出厂设置,在某一天我们不用之后也会删除一些自己的信息数据来保护隐私
C++中的面向对象来源于生活,每个对象也会有初始设置以及对象销毁前的清理数据的设置

构造函数和析构函数(重点)

对象的初始化和清理也是两个非常重要的安全问题
一个对象或者变量没有初始状态,对其使用后果是未知的
通过杨使用完一个对象或者变量,没有及时清理,也会造成一些安全问题

C+++利用了构造函数和析构函数解决上述问题,这两个函数将会被自动编译器自动调用,完成对象初始化和清理工作。
对象的初始化和清理工作是编译器强制要我们做的事情,
因此如果我们不提供构造和析构,编译器会提供的构造和析构函数是空实现
(就是说一行代码都没有 里面是空的)

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

构造函数语法啊:类名(){}
1.构造函数没有返回值也不写void
2.函数名称与类名相同
3.构造函数可以有参数,因此可以发生重载
4.程序在调用对香港时候会自动调用构造,无须手动调用,而且只会调用一次

析构函数语法: ~类名(){}
1.析构函数,没有返回值也不写void
2..函数名称与类名相同,在名称前加上符号~
3.析构哈桉树不可以有参数因此不可以发生重载
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
53
54
55
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

/*对象的初始化和清理*/
/*构造函数进行初始化的操作*/
class Person
{
public:
/*构造函数*/
/*没有返回值 不用void
函数名 = 类名 可以有参数可以重载
创建对象的时候自动调用*/
Person()
{
cout << "构造函数的调用" << endl;
}
/*不写的话编译器就自动生成一个无内容的*/

/*析构函数进行清理的操作
没有返回值不写void
函数名和类名相同 前面加~
析构函数不可以有参数 无法重载
对象销毁前自动调用析构函数 只会调用一次*/
~Person()
{
cout << "person 的析构 函数调用" << endl;
}
};



void test01()
{
cout << "现在进入了test01" << endl;
Person p;
}
/*为什么这里要通过test01执行
因为test01作为栈上的函数 执行完之后内部的东西全部会销毁
这样的话内部创建的person也会被销毁
所以就会调用析构函数 如果只是在main里面创建person
就没办法展示析构函数调用的部分*/

int main() {

Person p1;

test01();

system("pause");

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

using namespace std;

/*构造函数的分类以调用
分类
按照参数分类
可以分为有参构造函数和无参构造函数(默认构造函数)
按照 类型来分 普通构造和拷贝构造*/
class Person
{
public:
int age;

public:
Person()
{
cout << "Person构造函数调用" << endl;
}
Person(int a)
{
age = a;
cout << "Person有参构造函数调用" << endl;
}
/*拷贝构造函数*/
Person(const Person &p)
{/*传入了一个person类的数据
把传入的person类上的所有属性拷贝到这个构造的person类中
这里加上const 保证不会修改原始的要被拷贝的数据 然后通过引用的方式传入*/

/*将传入的人身上的所有属性拷贝到我身上*/
age = p.age;
cout << "Person拷贝构造函数调用" << endl;
}

~Person()
{
cout << "person 析构函数调用" << endl;
}


};
/*调用*/
void test01()
{
/*括号法*/
Person p;
/*默认构造函数调用*/
Person p2(10);
/*调用有参构造函数*/
Person p3(p2);
/*调用拷贝构造函数*/

/*注意事项
调用默认构造函数的时候不要加()*/
//person p4();
/*如果写成这样 没有发生任何事
因为上面的代码 编译器会认为是一个函数的声明
所以不会认为在创建对象*/

cout << "age of p2: " << p2.age << endl;
cout << "age of p3: " << p3.age << endl;

/*显示法*/
Person p5;
Person p6 = Person(10);
Person p7 = Person(p6);
/*分别用显示法调用三种构造*/

Person(10);/*匿名对象
特点:当前行结束后,系统会立刻回收掉匿名对象*/
cout << "匿名对象的下一行" << endl;

/*不要利用拷贝构造函数初始化匿名对象*/
/*Person(p3); 编译器会认为person(p3) === person p3;
* 编译器认为这是一个对象的声明
*/
/*隐式转换法*/
Person p8 = 10;
/*相当于 写了 Person p4 = Person(10);*/
Person p9 = p8; /*隐式的拷贝构造*/
}

int main() {

test01();

system("pause");

return 0;
}

拷贝构造函数调用时机

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

using namespace std;

/*拷贝构造函数调用时机*/



class Person
{
public:
int age;

Person()
{
cout << "person默认构造函数调用" << endl;
}
Person(int tage)
{
age = tage;
cout << "person有参构造函数调用" << endl;
}
Person(const Person &p)
{
age = p.age;
cout << "person拷贝构造函数调用" << endl;
}

~Person()
{
cout << "person 析构函数调用" << endl;
}

};





//1\使用一个一集创建完毕的对象来初始化一个新对象
void test01()
{
Person p1(20);
Person p2(p1);
cout << "p2 age:" << p2.age << endl;
}

//值传递的方式给函数参数传值
void doWord(Person p)
{
/*其实这里就是在调用拷贝函数创建一个新的p*/
}

void test02()
{
Person p3;
doWord(p3);
}
//以值方式返回局部对象
Person doWork2()
{
Person p1;
/*这里的p1式一个局部变量
生命周期只在函数 内部 没办法超过这个函数
所以下面return的时候 其实是调用拷贝函数拷贝出了一个新的对象*/
return p1;
}

void test03()
{
Person p = doWork2();
}



int main() {
test01();
test02();
test03();

system("pause");

return 0;
}

构造函数调用规则(重要)

默认情况想爱爱,c++的编译器至少给一个类添加三个函数
1、默认构造函数 无参 函数体为空
2、默认析构函数 无参 函数体为空
3、默认拷贝构造函数 对属性值进行拷贝

构造函数调用规则如下:
如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造
如果用户定义拷贝构造函数,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
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
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

//构造函数的调用规则
/*创建一个类 cpp会给每个类都添加至少三个函数*/

//默认构造 空实现

//析构函数

//拷贝构造

class Person
{
public:

int Page;
Person()
{
cout << "person 的默认函数调用" << endl;
}
/*如果把这个无参构造函数注释掉之后再调用函数test02就会报错
因为生成了有参构造函数之后编译器就不会再生成有参构造函数
但是依然会提供拷贝构造函数
如果单独写了 拷贝构造函数 那么编译器就不再提供任何其他构造函数*/

Person(int age)
{
cout << "person的有参构造函数" << endl;
Page = age;
}
//拷贝函数
Person(const Person& p)
{
Page = p.Page;
cout << "Person的拷贝构造函数调用" << endl;
}
/*写不写拷贝构造函数 下面的Person p2(p)都可以实现
因为如果你不写 他会生成一个默认的 功能也是把一个改类对象的值赋给另一个对象*/

~Person()
{
cout << "person 的析构函数调用" << endl;
}
};

void test01()
{
Person p;
p.Page = 18;

Person p2(p);
cout << "p2的年龄为" << p2.Page << endl;
}

/*写了有参构造函数之后编译器就不会再自动提供默认构造函数*/
void test02()
{
Person p1;
}

int main() {
test01();

system("pause");

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

using namespace std;

/*深拷贝和浅拷贝*/
class Person
{
public:
int age;
int* pheight;//身高

Person()
{
cout << "调用无参构造函数" << endl;
}
Person(int a)
{
cout << "调用有参构造函数" << endl;
}
Person(int a,int h)
{
age = a;
pheight = new int(h);
/*把数据开辟到堆区*/
cout << "调用有参构造函数" << endl;
}

//自己实现拷贝构造函数 解决潜拷贝带来的问题
Person(const Person& p)
{
age = p.age;
cout << "调用拷贝构造函数" << endl;
//pheight = p.pheight;
//编译器默认实现的就是上面这行代码

//深拷贝操作
pheight = new int(*p.pheight);
/*这行代码确保了每个拷贝出来的person 的height
都可以指向一块属于自己的height的内存 而不是全部指向同一块*/
}
~Person()
{
/*通过析构函数释放内存*/
if (pheight != NULL)
{
delete pheight;
pheight = NULL;
/*先把内存释放干净
再防止野指针出现 确保指针指向空*/
}
cout << "调用析构函数" << endl;
}

};

void test01()
{
//Person p1(18);
//cout << "age of p1" << p1.age << endl;
//Person p2(p1);
//cout << "age of p2" << p2.age << endl;
///*如果把手动定义的拷贝构造函数删除,
//然后执行上面的代码,他就会默认执行一个
//浅拷贝的操作*/

Person p1(18, 160);
cout << "age of p1" << p1.age << "height of p1:"<< *p1.pheight << endl;

Person p2(p1);
cout << "age of p2" << p2.age << "height of p2:" << *p2.pheight << endl;

}

int main() {

test01();

system("pause");

return 0;
}

总结:如果属性有再堆区开辟,那么一定要自己提供拷贝构造函数,防止浅的拷贝带来的问题

image-20241224093024989

初始化列表

作用
C++提供了初始化列表语法,用来初始化属性
语法:构造函数()属性1{值1},属性2{值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
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

//初始化列表
class Person
{
public:

int a;
int b;
int c;

/*无参构造*/
//Person()
//{

//}

//传统初始化操作
/*通过构造函数赋初始值*/
//Person(int a1, int b1, int c1)
//{
// a = a1;
// b = b1;
// c = c1;
//}

/*初始化列表初始化属性*/
Person():a(10),b(20),c(30)
{
}
/*上面的办法可行 但是问题是只能赋10 20 30
写成下面的写法可以*/
Person(int a1,int b1,int c1) :a(a1), b(b1), c(c1)
{
}
/*注意记住初始化列表的语法*/
};

void test01()
{
//Person p(10, 20, 30);
//cout << "a: " << p.a << endl;
//cout << "b: " << p.b << endl;
//cout << "c: " << p.c << endl;
/*通过传统方法赋值并输出*/

Person p1;
cout << "p1 a: " << p1.a << endl;
cout << "p1 b: " << p1.b << endl;
cout << "p1 c: " << p1.c << endl;

Person p2(30, 20, 10);
cout << "p2 a: " << p2.a << endl;
cout << "p2 b: " << p2.b << endl;
cout << "p2 c: " << p2.c << endl;
}

int main() {
test01();

system("pause");

return 0;
}

类对象作为类的成员

C++类中的成员可以是另一个类的对象

1
2
3
class A {}
class B
{A a1;}

B类中有对象A作为成员,A为对象成员
那么当创建B对象时,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
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
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

/*类对象作为类成员*/

class Phone
{
public:
string brand;

Phone(string name)
{
brand = name;
cout << "phone的构造函数的调用" << endl;
}
~Phone()
{
cout << "phone的析构函数的调用" << endl;
}
};

class Person
{
public:
/*姓名 手机*/
string Pname;

/*隐式转换法
* 使用下面的初始化列表的时候
* 编译器自动转换成下面的代码
* 所以可以直接把字符串类型赋值给下面的phone对象
Phone m_Phone = pName*/

/*使用初始化列表调用*/
Phone Pphone;
Person(string name, string phone):Pname(name),Pphone(phone)
{
cout << "person构造函数的调用" << endl;
}
~Person()
{
cout << "person 析构函数的调用" << endl;
}

};

void test01()
{
Person p("张三", "苹果");

cout << p.Pname << "拿着:" << p.Pphone.brand << endl;

}
int main() {

test01();

system("pause");

return 0;
}

静态成员(非常重要)

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

静态成员变量
1 所有对象共享同一份数据
2 在编译阶段分配内存
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
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
#include <iostream>
#include <string>
#include <ctime>
using namespace std;

/*静态成员变量*/
class Person
{
public:
static int P_A;
/*加上static之后就变成静态成员了
对于静态成员
1、所有对象都共享同一份数据
2、编译阶段就分配了内存
3、在类内声明 在类外进行 初始化操作*/

/*静态成员变量也是有访问权限的*/
private:
static int P_B;

};

int Person::P_A = 100;
/*通过这种格式写
类名::静态变量
这样就相当于在类内做了声明 在类外做了初始化
Person::的意思 作用域 作用域的范围是Person类内部*/

int Person::P_B = 300;

void test01()
{
Person p;
cout << p.P_A << endl;
/*一开始报错 没有初始化*/

Person p2;
p2.P_A = 200;
cout << p.P_A << endl;
/*这里修改的是p2 的值
然后访问的是原来的p 发现p的值也改变了
说明两个实例的P_A其实是同一个东西 */
}

void test02()
{
/*静态成员变量 不属于某个对象上
所有对象都共享同一份数据
因此静态成员变量有两种访问方式*/
cout << "test02" << endl;
/*通过对象进行访问*/
Person p3;
cout << p3.P_A << endl;

/*通过类名进行访问*/
cout << Person::P_A << endl;
/*通过类名访问的时候要用作用域的符号 而不是.*/
//cout << Person::P_B << endl;
/*无法访问 因为是private*/

}

int main() {

test01();
test02();

system("pause");

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

/*静态成员函数*/
/*所有对象共享同一个函数
静态成员函数只能访问静态成员变量 */

class Person
{
public:

static int pa;
int pb;

static void func()
{
cout <<"func 中 修改前" << pa << endl;
pa = 20;
cout << "func 中 修改后" << pa << endl;
cout << "static void func调用" << endl;

//pb = 10;
/*上述代码报错
因为pb是非静态成员变量
这个func是一个静态成员函数
只能访问静态成员变量

原理 无法区分到底是哪个对象的pb
对于pa 因为是共享的 所有对象共用 所以可以区分*/
}


/*静态成员函数也是有访问权限的*/
private:
static void func2()
{
cout << "调用func2" << endl;
}

public:
void func3()
{
func2();
}
};

int Person::pa = 10;


/*有两种访问方式*/
void test01()
{
/*通过对象进行访问*/
Person p;
p.func();

/*通过类名进行访问*/
Person::func();

p.func3();
/*通过这种方式可以调用func2*/
}



int main() {

test01();

system("pause");

return 0;
}

C++对象模型和this指针

成员变量和成员函数分开存储

在c++中,类内的成员变量和成员函数分开存储
只有静态成员变量才属于类的对象上
空对象的大小是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
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

/*成员变量和成员函数是分开存储的*/
class Person
{
int pa;
int pc;
/*非静态成员变量 属于类的对象上*/
static int pb;
/*静态成员变量 不属于类的对象上*/

/**/
void func()
{
/*非静态成员函数 内存还是不属于类的对象上*/
}

static void func()
{
/*静态成员函数 内存还是不属于类的对象上*/
}

/*只有非静态成员变量属于类的对象上*/

};
int Person::pb = 10;


void test01()
{
Person p;
/*空对象占用内存空间为*/
/*cpp编译器会给每个空对象也分配一个字节空间
是为了区分空对象占内存的位置*/
/*每个空对象也应该有一个独一无二的内存地址*/
cout << "size of p " << sizeof(p) << endl;
}

void test02()
{
Person p;
cout << "size of p " << sizeof(p) << endl;
}

int main() {
// test01();
test02();
system("pause");

return 0;
}

this指针概念

通过3.2.1我们知道在C++中成员变量和成员函数是分开存储的
每一个非静态成员函数只会诞生一份函数实例,就是说多个同类型的对象会共用一块代码
那么问题是:这一块代码是如何区分哪个对象调用自己的呢?

C++通过提供特殊的对象指针,this指针,解决上述问题。this指针指向被调用的成员函数所属的对象。

this指针式隐含每一个非静态成员函数内的一种指针
this指针不需要定义,直接使用即可

this指针的用途:
当形参和成员变量同名的时候,可用this指针来区分
在类型非静态成员你函数中返回对象本身,可使用return *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
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
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

/*this 指针*/

class Person
{
public:
int age;
Person(int age)
{
this->age = age;
/*原本这里会有名称冲突*/
/*this 指针指向的是被调用的成员函数所属的对象*/
}

Person& PersonAddAge(Person &p)
{
this->age += p.age;
/*this->age++;*/
/*this 指向p2指针 而*this 指向p2这个实例*/
return *this;

}
void PersonAddAge1()
{
this->age += 1;
}

};


/*解决名称冲突*/
void test01()
{
Person p1(18);
cout << "p1的年龄为" << p1.age << endl;
}


/*返回对象本身用*this*/

void test02()
{
Person p2(10);
Person p3(20);

p2.PersonAddAge1();
cout << "age of p2:" << p2.age << endl;

p2.PersonAddAge(p3);
cout << "age of p2:" << p2.age << endl;

/*链式编程思想*/
p2.PersonAddAge(p3).PersonAddAge(p3);
cout << "age of p2:" << p2.age << endl;
}


int main() {

/*test01();*/
test02();

system("pause");

return 0;
}

空指针访问成员函数

C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针

如果用到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
43
44
45
46
47
48
49
50
51
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

/*空指针调用*/

class Person
{
public:
void showClassName()
{
cout << "this is Person class" << endl;
}
int age;

void showPersonAge()
{

if (this == NULL)
{
return;
}/*提高代码健壮性
如果为空就直接返回掉
如果不为空再输出*/
cout << "age = " << this->age << endl;
/*此处调用p指针时 this指针是一个空指针
所以无法访问里面的属性*/
}

};

void test01()
{
Person* p = NULL;

p->showClassName();
p->showPersonAge();
}


int main() {

test01();


system("pause");

return 0;
}

const 修饰成员函数

常函数
成员函数后加const后我们称为这个函数为常函数
常函数内不可以修改成员变量
成员属性声明时加关键字mutable后,再常函数中依然可以修改

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

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

using namespace std;

/*常函数*/

class Person
{
public:
void showPerson() const
{
/*this指针的本质是一个指针常量
指针的指向是不可以修改的
Person * const this
如果函数加上const
整个指针就不能动了
在成员函数后面加const 修饰的是this指向
让指针指向的值也不可以修改*/
//this->a = 100;

this->b = 100;

}

void func()
{

}

int a;
mutable int b;/*特殊变量 即使在常函数中也可以修改这个值*/

};

void test01()
{
Person p;
p.showPerson();
}

void test02()
{
const Person p1;
//p1.a = 10;
/*无法跟跟更改*/
p1.b = 20;
/*mutable 修饰的成员变量是一个特殊值
即使在常对象下也可以修改*/

/*常对象只能调用常函数*/
p1.showPerson();
//p1.func(); 无法调用

}




int main() {

test01();

system("pause");

return 0;
}

友元(重要)

生活中你的家有客厅(public),有你的卧室(private)
客厅所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去
但是呢,你也可以允许你的朋友进去

在程序里,有些私有属性,也想让类外特殊的一些函数或者类访问,就需要用到友元的技术

友元的木盾就是让一个函数或者类访问另一个类中私有成员

友元的关键字为friend

友元友三种实现
全局做友元
类做友元
成员函数做友元

友元单向不被继承

全局函数做友元

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

using namespace std;
/*全局函数做友元*/

class Building
{
friend void GoodBoy(Building& building);
/*这里就是在添加一个友元函数的声明
这里的意思就是Building认为 goodboy是他的好朋友
所以让goodboy访问builiding 的私有成员
注意这个声明是包含参数列表的
我认为是添加参数列表的目的可能是用来区分重载函数吧*/
public:
string LivingRoom;/*客厅*/

private:
string bedroom;

public:
Building()
{
this->LivingRoom = "客厅";
this->bedroom = "卧室";
}


};

void GoodBoy(Building & building)
{
cout << "好基友的全局函数 正在访问" << building.LivingRoom << endl;

/*上面是公共的变量 都可以访问,下面是私有的变量
原本无法访问 然后添加一个友元函数的声明在类内*/
cout << "好基友的全局函数 正在访问" << building.bedroom << endl;
/*在类的开头加上friend语句只有就可以访问了*/
}

int main() {

Building b1;
GoodBoy(b1);

system("pause");

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

/*类做友元*/

class Building;

class Building
{
friend class GoodGay;
/*goodgay类是本类的好朋友,可以访问本类中私有的成员*/
public:
string LivingRoom;
private:
string BedRoom;
public:
Building();
};

/*类的声明*/
class GoodGay
{
public:
Building * building;

void visit();
/*参观函数访问buidling类中的属性*/

GoodGay();

};

GoodGay::GoodGay()
{
/*创建一个建筑物对象*/
building = new Building;

}


void GoodGay::visit()
{
cout << "好基友类正在访问" << building->LivingRoom << endl;
cout << "好基友类正在访问" << building->BedRoom << endl;
}



/*类外写成员函数*/
Building::Building()
{
LivingRoom = "客厅";
BedRoom = "卧室";
}

void test01()
{
GoodGay g1;
g1.visit();
}


int main() {

test01();

system("pause");

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
58
59
60
61
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

// 定义 GoodGay 类
class GoodGay;

class Building
{
// 声明 GoodGay::visit 为友元函数
friend void GoodGay::visit();

public:
string LivingRoom;

private:
string BedRoom;

public:
Building()
{
this->LivingRoom = "客厅";
this->BedRoom = "卧室";
}
};

// 定义 GoodGay 类
class GoodGay
{
public:
GoodGay()
{
building = new Building;
}

Building* building;

void visit()
{
// 访问 Building 类的私有成员 BedRoom,因为 GoodGay::visit 是友元函数
cout << "visit函数正在访问: " << building->LivingRoom << endl;
cout << "visit函数正在访问: " << building->BedRoom << endl; // 这里可以访问 BedRoom
}

void visit2()
{
// 只能访问公共成员 LivingRoom,不能访问 private 的 BedRoom
cout << "visit2函数正在访问: " << building->LivingRoom << endl;
}
};

int main() {
GoodGay g1;
g1.visit(); // 访问 LivingRoom 和 BedRoom

system("pause");
return 0;
}

理论上可以访问 实际上代码出错 问题暂时还没有解决 下次可以问问老师

友元定义的三种方式

1
2
3
4
5
6
/*类做友元*/
friend class GoodGay;
/*全局函数做友元*/
friend void GoodBoy(Building& building);
/*成员函数做友元*/
friend void GoodGay::visit();

运算符重载

运算符重载概念:对已有的运算符重新定义,赋予其另一种功能,来适应不同的数据类型

加号运算符重载

作用:实现了两个自定义数据类型相加的运算

image-20241225174450781

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 <string>
#include <ctime>

using namespace std;

/*加号运算符重载*/

/*成员函数重载*/
class Person
{
public:
int a;
int b;
/*成员函数重载*/
//Person operator+(Person& p)
//{
// Person temp;
// temp.a = this->a + p.a;
// temp.b = this->b + p.b;
// return temp;
//}

};

/*全集函数重载
*/
Person operator+(Person& p1, Person& p2)
{
Person temp;
temp.a = p1.a + p2.a;
temp.b = p1.b + p2.b;
return temp;
}
/*写一个重载的版本*/
Person operator+(Person& p1, int num)
{
Person temp;
temp.a = p1.a + num;
temp.b = p1.b + num;
return temp;
}

void test01()
{
Person p1;
p1.a = 10;
p1.b = 10;

Person p2;
p2.a = 10;
p2.b = 10;

/*函数方便调用*/
Person p3 = p1 + p2;
/*成员函数本质调用*/
//Person p3 = p1.operator+(p2);
//Person p3 = operator+(p1, p2);

cout << "a of p3 = " << p3.a << endl;
cout << "b of p3 = " << p3.b << endl;

p3 = p3 + 10;
cout << "a of p3 = " << p3.a << endl;
cout << "b of p3 = " << p3.b << endl;

}




int main() {
test01();

system("pause");

return 0;
}

其他运算符的重载

这里就不展开多说了,基本上都是一样的
可以重载的运算符包括

1
2
3
4
5
左移运算符<<(左移运算符只能利用全局函数重载)
递增运算 ++
赋值运算符 =
关系运算符 ><=!=
函数调用运算符 ()

继承

继承是面向对象三大特性之一
有些类和类之间存在特殊关系,例如下图中:
image-20241225203438189

我们发现,定义这些类时,下级的成员除了拥有上一级的共性,还有自己的特性。
这个时候我们就可以考虑利用继承的技术,减少重复代码

继承的基本语法

例如我们看到很多网站中 ,都有公共的头部,公共的底部,甚至公共的左侧列表,只有中心内容不同,系欸下啊来我们分别利用普通写法和继承写法来实现页面中的内容,看一下继承存在的意义以及好处。

语法: class 子类名 :继承方式 父类名
子类也叫派生类
父类也叫基类

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

using namespace std;

/*普通实现页面*/

///*java页面*/
//class Java
//{
//public:
// void header()
// {
// cout << "首页、公开课、登陆(公共头部)" << endl;
// }
// void footer()
// {
// cout << "底部内容" << endl;
// }
//
// void left()
// {
// cout << "java python c++等连接(公共分类列表)" << endl;
// }
// void content()
// {
// cout << "java学科视频" << endl;
// }
//
//};
//
//
//
//
///*python页面*/
//class Python
//{
//public:
// void header()
// {
// cout << "首页、公开课、登陆(公共头部)" << endl;
// }
// void footer()
// {
// cout << "底部内容" << endl;
// }
//
// void left()
// {
// cout << "java python c++等连接(公共分类列表)" << endl;
// }
// void content()
// {
// cout << "python学科视频" << endl;
// }
//
//};
//
//void test01()
//{
// cout << "java下载视频的页面如下" << endl;
// Java j1;
// j1.header();
// j1.content();
// j1.left();
// j1.footer();
//
// cout << "=======分割==========" << endl;
//
// Python p1;
// p1.header();
// p1.content();
// p1.left();
// p1.footer();
//}


/*继承实现页面*/
/*先写一个公共的页面*/
class BasePage
{
public:
void header()
{
cout << "首页、公开课、登陆(公共头部)" << endl;
}
void footer()
{
cout << "底部内容" << endl;
}

void left()
{
cout << "java python c++等连接(公共分类列表)" << endl;
}
};

/*java python页面 */
class Java : public BasePage
{
public:
void content()
{
cout << "java学科视频" << endl;
}
};

class Python : public BasePage
{
public:
void content()
{
cout << "python学科视频" << endl;
}
};

void test01()
{
cout << "java下载视频的页面如下" << endl;
Java j1;
j1.header();
j1.content();
j1.left();
j1.footer();

cout << "=======分割==========" << endl;

Python p1;
p1.header();
p1.content();
p1.left();
p1.footer();
}

/*继承的好处
减少重复的代码
语法 */
int main() {

test01();


system("pause");

return 0;
}

总结

继承的好处:可以减少重复 的代码
class A : public B;
A类称为子类或者派生类
B类称为父类或者基类

派生类中的成员,包含两大部分:

一类是从基类继承过来的,一类是自己增加的成员。
从基类继承过来的表现 出共性 自己新增的表现出个性

继承方式

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

继承方式一共有三种
公共继承
保护继承
私有继承

image-20241225211053012

image-20241230174832884

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

using namespace std;

/*继承方式*/

/*公共继承*/
class Base1
{
public:
int a;
protected:
int b;
private:
int c;

};

class Son1 :public Base1
{
public:
void func()
{
a = 10;
/*父类中的公共权限成员到子类中依然是公共权限*/
b = 10;
/*父类中的保护权限成员 到子类中依然是保护权限*/
//c = 10;
/*父类中 的私有权限成员 子类中访问不到*/
}
};


void test01()
{
Son1 s1;
s1.a = 100;
//s1.b = 100;
/*b是保护权限 类外不可以访问*/
}

/*保护继承*/
class Base2 :public Base1
{
public:
int a;
protected:
int b;
private:
int c;
};
class Son2 :protected Base2
{
public:
void func()
{
a = 100;
b = 100;
/*父类中的公共成员和保护成员
到子类中都变成保护权限*/

//c = 100;
/*私有成员访问不到*/
}

};



void test02()
{
Son2 s2;
//s2.a = 1000;
/*报错 在son2中 a变成了保护权限
所以类外访问不到*/
}


/*私有继承*/
class Base3
{
public:
int a;
protected:
int b;
private:
int c;
};

class Son3 :private Base3
{
public:
void func()
{
a = 100;/*父类中的公共成员 到子类中变成私有
父类中保护成员 子类中变为私有成员*/
b = 100;
c = 100;
/*父类中的私有成员 子类中访问不到*/
}
};

void test03()
{
Son3 s3;
//s3.a = 1000;
//s3.b = 1000;
/*到了son3中变成了私有的成员 类外访问不到*/

}

class GrandSon3 :public Son3
{
public:
void func()
{
//a = 1000;
/*还是私有 访问不到*/
/*son3中变为私有 即使是son3的子类也访问不到*/
}
};

int main() {

system("pause");

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

using namespace std;

/*继承中的对象模型*/

class Base
{
public:
int a;
protected:
int b;
private:
int c;
};

class Son :public Base
{
public:
int d;
};

void test01()
{
Son s1;
cout << "size of s1: " << sizeof(s1) << endl;
}

/*父类中所有的非静态成员属性都会被子类继承下去
父类中私有的成员访问不到 但是确实被继承了*/

int main() {

test01();

system("pause");

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

using namespace std;

/*继承中的构造和析构顺序*/

class Base
{
public:
Base()
{
cout << "父类中的构造函数" << endl;
}
~Base()
{
cout << "父类中的析构函数" << endl;
}

};

class Son : public Base
{
public:
Son()
{
cout << "子类中的构造函数" << endl;
}
~Son()
{
cout << "子类中的析构函数" << endl;
}
};

void test01()
{
Son s1;

}



int main() {

test01();

system("pause");

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
 #include <iostream>
#include <string>
#include <ctime>

using namespace std;

/*继承同名成员处理方式*/

class Base
{
public:
int a;
Base()
{
a = 100;
}
void func()
{
cout << "base下func" << endl;
}
};

class Son :public Base
{
public:
int a;
Son()
{
a = 200;
}
void func()
{
cout << "son下func" << endl;
}
void func(int c)
{
cout << "son下func" << endl;
}

};
/*同名成员对象处理方式*/
void test01()
{
Son s;
cout << "son 下的a= " << s.a << endl;
cout << "base 下的a= " << s.Base::a << endl;

s.func();
s.Base::func();


s.Base::func(1);
/*如果子类中出现了和父类中的同名函数
那么子类中会隐藏掉父类中所有的同名成员函数
如果想要访问都要加作用域*/

}


/*同名成员函数处理方式*/
void test02()
{

}
int main() {

test01();
system("pause");

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
58
59
60
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

/*继承 */
class Base
{
public:
static int a;

static void func()
{
cout << "父类的静态函数" << endl;
}
};
int Base::a = 100;

class son :public Base
{
public:
static int a;
static void func()
{
cout << "子类的静态函数" << endl;
}
};

void test01()
{
/*通过对象访问*/
son s1;
cout << "son 的 a" << s1.a << endl;
cout << "base 的 a" << s1.Base::a << endl;
/*通过类名访问*/
cout << "son 的 a" << son::a << endl;
cout << "base 的 a" << son::Base::a << endl;
cout << "base 的 a" << Base::a << endl;


/*通过对象访问*/
s1.func();
s1.Base::func();

/*通过类名访问*/
son::func();
son::Base::func();


}

int son::a = 10;
int main() {
test01();

system("pause");

return 0;
}

多继承语法

c++允许一个类继承多个类

语法:class 子类 :继承方式 父类1,继承方式 父类2

多继承可能会引发父类中同名成员出现,需要加作用域区分

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

using namespace std;

/*多继承语法*/
class Base1
{
public:
int a;
Base1()
{
a = 100;
}
};

class Base2
{
public:
int a;
Base2()
{
a = 200;
}
};
/*语法 class 子类 :继承方式1 父类1 继承方式2 父类2*/
class Son :public Base1, public Base2
{
public:
int c;
int d;
Son()
{
c = 300;
d = 400;
}
};

void test01()
{
Son s;

cout << "子类所占的空间" <<sizeof(s)<< endl;

/*当父类中出现了同名的成员,需要加作用域区分*/
cout << "base1的a" << s.Base1::a << endl;
cout << "base2的a" << s.Base2::a << endl;
}




int main() {
test01();

system("pause");

return 0;
}

总结:多继承中如果父类出现了同名的情况,子类使用的时候需要加作用域

菱形继承 虚基类 虚继承(重要重要)

菱形继承概念
两个派生类继承同一个基类
又有某个类同时继承两个派生类
这种继承被称为菱形继承,或者钻石继承

经典菱形继承案例

image-20241225225406058

菱形继承问题

羊继承了 动物的数据,鸵同样继承了动物的数据,当羊驼使用数据时,就会产生二义性
羊驼继承自动物的数据继承了两份,其实我们应该清楚,这份数据我们只需要一份

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

using namespace std;

/*动物类*/
class Animal
{
public:
int age;

};


/*利用虚继承可以解决菱形继承的问题*/
/*继承之前加上关键字virtual变为虚继承*/
/*animal类称为虚基类*/
class Sheep :virtual public Animal
{

};

class Camel:virtual public Animal
{

};

class Alpaca :public Camel, public Sheep
{

};

void test01()
{
Alpaca a1;
a1.Sheep::age = 18;
a1.Camel::age = 28;
/*虚继承之后共享数据 把两个数据改为一个*/

/*当菱形继承的时候有两个父类具有相同的数据
需要加以区分*/
cout << "a1.Sheep::age " << a1.Sheep::age << endl;
cout << "a1.Camel::age " << a1.Camel::age << endl;
cout << "a1.age " << a1.age << endl;

/*这份数据我们知道只要有一份就行菱形继承导致数据有两份
资源浪费*/
/*虚继承继承下来的是一个指针 指向虚基类*/


}



int main() {

test01();

system("pause");

return 0;
}

总结
菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义
利用虚继承可以解决菱形继承问题

多态

多态的基本概念

多态是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
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
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

/*多态*/

/*动物类*/
class Animal
{
public:
/*虚函数*/
virtual void speak()
{
cout << "动物在说话" << endl;
}
};

class Cat :public Animal
{
public:
void speak()
{
cout << "小猫在说话" << endl;
}
};

class Dog :public Animal
{
public:
void speak()
{
cout << "狗叫" << endl;
}
};

/*执行说话的函数*/
/*地址早绑定 在编译阶段就能确定函数地址*/
/*如果想要猫说话 那么这个地址就不能提前绑定 需要在运行的时候绑定
也就是地址晚绑定*/
void doSpeak(Animal &animal)
{/*c++中允许父类子类直接类型转换*/
animal.speak();
}

void test01()
{
Cat c1;
doSpeak(c1);

Dog d1;
doSpeak(d1);
}

/*动态多态满足条件
1、有继承关系
2、子类重写父类的虚函数

动态多态的使用
父类的指针或者引用 指向子类的对象

*/

int main() {
test01();

system("pause");

return 0;
}

重点:动态多态满足条件
1、有继承关系
2、子类重写父类的虚函数

使用条件:父类的指针或者引用指向子类的对象

原理:

image-20241226002720479

多态案例一 计算器类

案例描述 :

分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算器类

多态的优点:
代码组织结构清晰
可读性强
利于前期和后期的扩展以及维护

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

using namespace std;

/*分别利用普通写法和多态技术实现计算器*/

/*普通写法*/
class Calculator
{
public:
int n1;
int n2;

int getResutl(string oper)
{
if (oper == "+")
{
return n1 + n2;
}
else if (oper == "-")
{
return n1 - n2;
}
else if (oper == "*")
{
return n1 * n2;
}
/*如果想要扩展新的功能 需要修改源码*/
/*在真的开发中 提倡开闭原则
对扩展进行开放 对修改进行关闭*/
}
};


/*利用多态实现计算器*/

/*多态好处
1、组织结构清晰
2、可读性强
3、前后期扩展可维护性高*/

/*实现计算器的抽象类*/
class AbstractCalc
{
public:
int n1;
int n2;
virtual int getResult()
{
return 0;
}
};

/*加法计算器类*/
class AddCalc :public AbstractCalc
{
public:
virtual int getResult()
{
return n1 + n2;
}
};

/*减法计算器*/
class SubCalc :public AbstractCalc
{
public:
virtual int getResult()
{
return n1 - n2;
}
};

/*乘法计算器*/
class MulCalc :public AbstractCalc
{
public:
virtual int getResult()
{
return n1 * n2;
}
};

void test01()
{
/*创建计算器对象*/
Calculator c1;
c1.n1 = 10;
c1.n2 = 20;
cout << "+" << c1.getResutl("+") << endl;
cout << "-" << c1.getResutl("-") << endl;
cout << "*" << c1.getResutl("*") << endl;
}

void test02()
{
/*加法*/
AbstractCalc* abc = new AddCalc;
abc->n1 = 10;
abc->n2 = 20;

cout << "+ " << abc->getResult() << endl;
/*用完后记得销毁*/
delete abc;
}

int main() {
test01();
test02();

system("pause");

return 0;
}

纯虚函数和抽象类(非常重要)

在多态中,通常父类中虚函数的实现是毫无意义的,主要调用子类重写的内容

因此可以将虚函数改为纯虚函数

纯虚函数语法:virtual 返回值类型 函数名 (参数列表) = 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
58
59
60
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

/*纯虚函数和抽象类*/
class Base
{
public:
/*纯虚函数*/
virtual void func() = 0;
/*只要有一个纯虚函数,这个类称为抽象类
抽象类特点:无法实例化对象*/

/*抽象类的子类必须要重写父类中的纯虚函数 不然也属于抽象类*/
};

class Son1 :public Base
{
public:


};

class Son2 :public Base
{
public:

virtual void func()
{
cout << "func函数调用" << endl;
}
};

void test01()
{
//Base b;
/*报错 不允许使用抽象类创建对象*/

//Son1 s1;
/*同样报错 因为继承了纯虚函数 所以也是抽象类*/

Son2 s1;
/*不报错 重写之后就正常了*/

Base* base = new Son2;
base->func();
}




int main() {
test01();

system("pause");

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

using namespace std;

/*多态案例2 制作饮品*/
class AbsDrink
{
public:
/*煮水冲泡倒入*/
virtual void boil() = 0;
virtual void brew() = 0;
virtual void Pour() = 0;
virtual void PutSth() = 0;

/*制作饮品*/
void makeDrink()
{
boil();
brew();
Pour();
PutSth();

}

};

/*制作咖啡*/
class Coffee :public AbsDrink
{
public:
virtual void boil()
{
cout << "煮农夫三拳" << endl;
}
virtual void brew()
{
cout << "冲咖啡" << endl;
}
virtual void Pour()
{
cout << "倒入" << endl;
}
virtual void PutSth()
{
cout << "加糖" << endl;
}
};

/*制作茶叶*/
class Tea :public AbsDrink
{
public:
virtual void boil()
{
cout << "煮矿泉水" << endl;
}
virtual void brew()
{
cout << "冲茶叶" << endl;
}
virtual void Pour()
{
cout << "倒入" << endl;
}
virtual void PutSth()
{
cout << "加枸杞" << endl;
}
};

/*制作饮品的函数*/
void doWork(AbsDrink &a)
{
a.makeDrink();
}
void doWork(AbsDrink *a)
{
a->makeDrink();
delete a;
}

void test01()
{
/*制作咖啡*/
Coffee c1;
doWork(c1);

doWork(new Tea);
}

int main() {
test01();

system("pause");

return 0;
}

虚析构和纯虚析构

多态使用时,如果 子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码

解决方式:将父类中的析构函数改为虚析构和纯虚析构

虚析构和纯虚析构的共性:
可以解决父类指针释放子类对象
都需要具体的函数实现

虚析构和纯虚析构的区别:
如果是虚析构,该类属于抽象类,无法实例化对象

虚析构语法:
virtual ~类名(){};
纯虚析构语法:
virtual ~类名() = 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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include <iostream>
#include <string>
#include <ctime>

using namespace std;

/*虚析构和纯虚析构*/
class Animal
{
public:
virtual void speak() = 0;

Animal()
{
cout << "Animal构造函数调用" << endl;
}
//virtual ~Animal()
//{
// cout << "Animal析构函数调用" << endl;
//}
/*利用虚析构可以结局 父类指针释放子类对象时不干净的问题*/

/*纯虚析构*/
virtual ~Animal() = 0;
};

Animal::~Animal()
{
cout << "Animal纯虚析构函数调用" << endl;
}
/*析构函数都需要可以实现,不然没有办法清楚该类的数据
万一该类有数据就无法清除 所以必须要有具体实现
有了纯虚析构之后这个类也属于抽象 类*/
class Cat :public Animal
{
public:

string *cname;
virtual void speak()
{
cout << *cname<<"喵" << endl;
}

Cat(string name)
{
cout << "cat struct func " << endl;
cname = new string(name);
}

~Cat()
{
if (cname != NULL)
{
cout << "~CAT running" << endl;
delete cname;
cname = NULL;
}
}


};


void test01()
{
Animal* a1 = new Cat("Tom");
a1->speak();
delete a1;
/*父类指针析构的时候不会调用子类中的析构函数导致子类如果有堆区属性
会出现内存泄露的情况*/
}

int main() {
test01();

system("pause");

return 0;
}

总结
虚析构和纯虚析构就是用来解决通过父类指针释放子类对象
如果子类中没有堆区数据,可以不写为虚构或者纯虚析构
拥有纯虚析构的类也属于抽象类

多态案例3 电脑组装

案例描述:

电脑主要组成部件为CPU 显卡 内存条
将每个零件疯转给出抽象基类 并且提供不同的厂商生产不同的零件
创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口
测试时组装三台不同的电脑进行工作

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

using namespace std;


/*电脑组装*/

/*抽象不同零件类*/
/*抽象cpu类*/
class CPU
{
public:
/*抽象的计算函数*/
virtual void calculate() = 0;

};
/*抽象GPU类*/
class GPU
{
public:
/*抽象的显示函数*/
virtual void display() = 0;

};
/*抽象内存类*/
class Memory
{
public:
/*抽象的计算函数*/
virtual void srorage() = 0;

};

/*电脑类*/
class Computer
{
public:
Computer(CPU* c, GPU* g, Memory* m)
{
c1 = c;
g1 = g;
m1 = m;
}
/*提供工作的函数*/
void work()
{
/*调用接口 */
c1->calculate();
g1->display();
m1->srorage();
}
/*提供析构函数释放三个电脑零件*/
~Computer()
{
/*释放*/
if (c1 != NULL)
{
delete c1;
c1 = NULL;
}
if (g1 != NULL)
{
delete g1;
g1 = NULL;
}
if (m1 != NULL)
{
delete m1;
m1 = NULL;
}
}
private:
/*三个零件 的指针*/
CPU* c1;
GPU* g1;
Memory* m1;

};


/*具体厂商*/
/*Intel*/
class IntelCPU :public CPU
{
public:
virtual void calculate()
{
cout << "英特尔的cpu开始计算了" << endl;
}
};
class IntelGPU :public GPU
{
public:
virtual void display()
{
cout << "英特尔的显卡开始超频了" << endl;
}
};
class IntelMemo :public Memory
{
public:
virtual void srorage()
{
cout << "英特尔的内存开始RGB了" << endl;
}
};
/*ASUS*/
class ASUSCPU :public CPU
{
public:
virtual void calculate()
{
cout << "ROG的cpu开始计算了" << endl;
}
};
class ASUSGPU :public GPU
{
public:
virtual void display()
{
cout << "ROG的显卡开始超频了" << endl;
}
};
class ASUSMemo :public Memory
{
public:
virtual void srorage()
{
cout << "ROG的内存开始RGB了" << endl;
}
};
/*lenovo*/
class lenovoCPU :public CPU
{
public:
virtual void calculate()
{
cout << "lenovo的cpu开始计算了" << endl;
}
};
class lenovoGPU :public GPU
{
public:
virtual void display()
{
cout << "lenovo的显卡开始超频了" << endl;
}
};
class lenovoMemo :public Memory
{
public:
virtual void srorage()
{
cout << "lenovo的内存开始RGB了" << endl;
}
};

void test01()
{
/*第一台电脑*/
CPU* intelCpu = new IntelCPU;
GPU* intelGpu = new IntelGPU;
Memory* intelMemo = new IntelMemo;

Computer* c1 = new Computer(intelCpu, intelGpu, intelMemo);
c1->work();
delete c1;

/*第二台电脑*/
CPU* cp2 = new ASUSCPU;
GPU* g2 = new ASUSGPU;
Memory* m2 = new ASUSMemo;

Computer* c2 = new Computer(cp2, g2, m2);
c2->work();
delete c2;
/*第三台电脑*/
CPU* cp3 = new lenovoCPU;
GPU* g3 = new lenovoGPU;
Memory* m3 = new lenovoMemo;

Computer* c3 = new Computer(cp3, g3, m3);
c3->work();
delete c3;

}

int main() {
test01();
system("pause");

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
#include <iostream>
using namespace std;
int f(int x);
int sum(int n)
{
int x, s = 0;
for (x = 0; x <= n; x++)
s += f(x);
return s;
}
int f(int x)
{
return (x * x + 1);
}

int main()
{
int a, b;
cout << "Enter an integer number: ";
cin >> a;
b = sum(a);
cout << a << ", " << b << endl;
return 0;
}

如果输入数字3,其输出结果是:
A、 3,12
B、 3,16
C、 3,18
D、 4,20

2有以下程序:

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

int f(int a, int b);
int main()
{
int i = 1, x;
x = f(i, i + 1);
cout << x << endl;
return 0;
}

int f(int a, int b)
{
int c;
c = a;
if (a > b)
c = 1;
else
c = -2;
return c;
}

**运行后的输出结果是:
A、 1
B、 0
C、 -1
D、 -2

3、若有以下函数调用语句,在此函数调用语句中实参的个数是

1
fun(a+b,(y=10,y),fun(n,k,d));

A、 3
B、 4
C、 5
D、 6

4、以下函数调用语句中,含有的实参个数是()
func(rec1,rec2 +1,(rec3,rec4));

A、 3
B、 4
C、 5
D、 有语法错误

以下程序的输出结果是

1
2
3
4
5
6
7
8
void fun(int a,int b,int c)
{ a=456;b=567;c=678;}
int main()
{ int x=10,y=20,z=30;
fun(x,y,z);
cout<<z<<”,”<<y<<”,”<<x<<endl;
}

A、 30,20,10
B、 10,20,30
C、 456,567,678
D、 678,567,456

C++语言中,形参与实参之间的数据传递方式是
A、 只能按值传递
B、 只能按引用传递

C、 既可以按值传递也可以按引用传递

D、 以上说法都不对

编程题练习

编程题:学生信息管理

题目描述:

设计一个类 Student,实现学生信息的管理功能。

数据成员:

  • name:学生的姓名(字符串类型)。
  • age:学生的年龄(整数类型)。
  • score:学生的成绩(浮点型)。

静态数据成员:

  • total_students:记录学生对象的总数量。

成员函数:

  • 构造函数:初始化学生姓名、年龄和成绩,并更新 total_students
  • 析构函数:销毁对象时,减少 total_students 的数量。
  • print_info():打印学生的基本信息(姓名、年龄、成绩)。

静态成员函数:

  • get_total_students():返回当前已创建的学生对象总数量。

主函数要求:

在主函数中:

  1. 创建若干个 Student 对象。
  2. 调用每个学生对象的 print_info() 函数输出学生信息。
  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
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
#include<iostream>
#include<string>
using namespace std;

class Student
{
public:
string name;
int age;
double score;
static int total_students;

Student()
{
}
Student(string m_name, int m_age, double m_score)
{
name = m_name;
age = m_age;
score = m_score;

total_students++;
cout << "正在调用学生类的构造函数" << endl;
}
~Student()
{
total_students--;
cout << "正在调用学生类的析构函数" << endl;
}
static int get_total_students()
{
return total_students;
}
void print_info()
{
cout << "学生信息" << endl;
cout << "name: " << name << "age: " << age << "score: " << score << endl;
}
};

int Student::total_students = 0;

int main()
{
//cout << "初始学生数量: " << Student::get_total_students() << endl;
//Student s1 = { "jack",18,90 };
//cout << "现在学生数量:" << s1.get_total_students() << endl;
//s1.print_info();
//Student s2 = { "mike",17,80 };
//cout << "现在学生数量:" << s2.get_total_students() << endl;
//s2.print_info();

/*改成由用户输入信息*/
int n;
cout << "请输入要定义的学生数量" << endl;
cin >> n;

/*检查输入是否有效*/
if (n < 1)
{
cout << "非法字符" << endl;
return 1;
}
else if (n > 10)
{
cout << "数量过多" << endl;
return 1;
}

//Student s1[n]; n不是常量 不能定义数组 要用动态数组
Student* s1 = new Student[n];
/*这就是定义一个动态数组*/

for (int i = 0; i < n; i++)
{
string name;
int age;
double score;

cout << "请输入第" << i+1 << "个学生的信息" << endl;

cout << "name:" << endl;
cin >> name;

cout << "年龄";
cin >> age;

cout << "得分";
cin >> score;

s1[i] = { name,age,score };
}

cout << "\n所有学生信息如下" << endl;
for (int i = 0; i < n; i++)
{
s1[i].print_info();
}




system("pause");
return 0;
}

ppt编程题例题1

定义一个复数类 Complex,重载运算符“+”,使之能用于复数的加法运算。参加运算的两个 运算量可以都是类对象,也可以其中有一个是整数,顺序任意。例如:c1+c2、i+c1 均合法。 (其中 i 是整数,c1、c2 是复数),编程实现求 2 个复数之和、整数与复数之和。 • 主函数如下: int main(){ int r1, i1, r2, i2 ; int i; cin >> r1 >> i1 >> r2 >> i2 >> i; Complex c1(r1, i1), c2(r2, i2), c3; c3 = c1 + c2; c3.display(); c3 = i + c1; c3.display(); return 0; }

ppt编程题例题2

声明一个Shape抽象类,由其派生出Point类,再由Point类派生出Circle类, 三个类均有area( ) 和shapeName()成员。 主函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main( ){
Point point(1,1);
Circle circle(1,1,5.6);
Shape *pt;
pt=&point;
pt->shapeName( );
cout<< ”x=” <<point.getX( )<<” , y=” << point.getY( ) << ” ,
pointarea=”<<pt->area( )<<endl;
pt=&circle;
pt->shapeName( );
cout<<”x=”<<circle.getX( )<<” , y=”<<circle.getY( )<<”, circlearea=”<<pt
>area( ) <<endl;
}

1
2
3
程序运行输出如下:
Point:x=1,y=1, pointarea=0
Circle:x=1,y=1, circlearea= 98.4

编程题例题4-Building类(非常重要)

• 实现一个Building建筑类, • 数据成员包括:编号id、名字name、建筑类型• 静态数据成员包括:总楼数total、下一个楼的编号next • 成员函数包括:构造函数、析构函数、打印信息函数print_info • 静态成员函数:getTotal() • 主函数、样例输入输出见下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
主函数
int main() {
string name, type;
Building* buildings[3];
for (int i = 0; i < 3; i++) {
cin >> name >> type;
buildings[i] = new Building(name, type);
}
cout << "Building counts: " << Building::getTotal() << endl;
for (int i = 0; i < 3; i++) {
buildings[i]->printInfo();
}
}
• 样例输入
A_building teach
B_building live
C_building teach
• 样例输出
Building counts: 3
id: 0 A_building, teach
id: 1 B_building, live
id: 2 C_building, teach
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>
#include<string>
using namespace std;

class Building
{
public:
int id;
string name;
string type;
static int total;
static int next;

Building() {
total++;
next++;
};
Building(string b,string c)
{
id = next;
name = b;
type = c;
total++;
next++;
}
~Building()
{
total--;
}
void PrintInfo()
{
cout << "id:" << id << " , " << name << "," << type << endl;
}
static int getTotal()
{
return total;
}
};
int Building::total = 0;
int Building::next = 0;

int main() {
string name, type;
Building* buildings[3];
for (int i = 0; i < 3; i++) {
cin >> name >> type;
buildings[i] = new Building(name, type);
}
cout << "Building counts: " << Building::getTotal() << endl;
for (int i = 0; i < 3; i++) {
buildings[i]->PrintInfo();
}
}


爷的代码,跑起来辣

image-20241227082008997

编程题例题5 Animal(非常重要)

请实现以下三个类:

一个动物Animal类,包含构造函数、数据成员年龄(age),成员函数getage()、纯虚函数speak()。

再由Animal类派生出两个类:猫Cat类、鸟Bird类。其中Cat类含有数据成员姓名(name);Bird类含有数据成员种类(kind),两个派生类都有speak()函数。

主函数参照以下程序执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int main(){
Animal *pa;
Cat ct("Garfield",2);
Bird bd("sparrow",1);
string x;
while(cin>>x){
if(x=="cat")
pa=&ct;
else if(x=="bird")
pa=&bd;
pa->speak();
}
return 0;
}
样例输入:
bird
cat
样例输出:
zizi~I'm a sparrow ,1-year-old.
Meow~I'm Garfield , 2-year-old.

代码

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
#include<iostream>
#include<string>
using namespace std;

class Animal
{
public:
int age;
Animal()
{}
~Animal()
{}
int getage()
{
return age;
}
virtual void speak() = 0;

};

class Cat :public Animal
{
public:
string name;
void speak()
{
cout << "喵" << endl;
}
Cat(string a, int b)
{
name = a;
age = b;
}
};

class Bird :public Animal
{
public:
string kind;
void speak()
{
cout << "鸣" << endl;
}
Bird(string a, int b)
{
kind = a;
age = b;
}
};




int main() {
Animal* pa = nullptr;
Cat ct("Garfield", 2);
Bird bd("sparrow", 1);
string x;
while (cin >> x) {
if (x == "cat") {
pa = &ct;
pa->speak();
}
else if (x == "bird")
{
pa = &bd;
pa->speak();
}

}
return 0;
}

image-20241227083958055