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
1946年世界上第一台电子计算机ENIAC诞生,机器语言和汇编语言都是与计算机的CPU相关的语言,后面出现了高级语言。

面向过程的程序设计又称为结构化程序设计。

面向对象的程序设计将数据和处理数据的过程当成一个整体——类和对象,并且具有特性:封装性、继承性、多态性。

集成开发环境:IDE 编辑器。

源程序——使用源语言编写的、有待翻译的程序。例:扩展名为.cpp的为C++源程序。

目标程序——源程序经翻译加工后所生成的程序。扩展名为.obj。

可执行程序——目标程序和所用的其他资源进行链接生成的可以直接运行的程序。扩展名为.exe。

翻译程序——指用来将源程序翻译为目标程序的工具。源程序是输入,目标程序是输出。

翻译程序分为3类:汇编程序、编译程序、解释程序。

链接程序——将目标程序与所需的其他资源进行链接生成可执行文件的程序。

C++程序由注释、编译预处理、程序主体组成。

函数通常都是由其他函数调用的,而main()函数比较特殊,程序在开始运行时会自动调用main()函数。

一个程序要执行时一定会先复制到内存,然后由CPU逐句读取过来再执行。

每个存储单元可以存放一个字节(8bit)数据,每个内存单元有一个唯一的地址。

如果一台计算机安装有256MB内存,它有256*1024*1024个内存单元。

数据区分为一下几种情况:

1、栈区,也叫堆栈区,用于存放程序函数中的局部变量。栈区中的变量也叫自动变量,用到某个函数时,该函数中定义的变量就保存在栈区,退出
函数时,相应的变量会自动释放,“先入栈的后出栈”。

2、全局变量区和静态变量区是存放长期数据的区域,当一个变量被定义为全局变量或者静态变量时,从程序开始执行到结束,它都会在内存中占有
固定的字节。

3、常量区一般是存放字符串常量的地方。

4、堆区,在程序执行过程中申请的内存空间属于堆区,这些申请的空间也应该在程序结束时释放。

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
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
1、字符集
字符集是构成C++语言的基本元素。英文字母、数字字符、特殊字符。
2、关键字
也成为保留字,是C++预定义的单词。
3、标识符
程序员声明的单词,用于命名。
4、分隔符
用于分隔程序中的正文。(){},;:
5、空白
(制表符、空格、空行等)

数据是程序处理的对象,不同类型的数据有不同的表示和处理方法,具有不同的运算规则。
内存单元的单位是字节,因此用来表示数据类型长度的单位也是字节。
整型int的长度取决于机器的字长,在16位机环境下,int的长度为2字节;在32位机环境下,int长度为4字节。但是short和long的长度是固定的,因此为提高程序的可移植性,应将整型数据声明为short或者long。

数据类型决定变量的存储方式和可以进行的操作,变量名称的作用就是区分不同的变量,变量被定义后系统自动为其分配应占的内存。

变量的初始化
int a = 10; int a(10); 都可以为变量赋值。

####typedef
可以为数据类型名字较长时定义一个简单的别名,也叫同义字,用来简化程序的输入。
使用规范 typedef unsigned short int UNIT16;
typedef并没有定义一个新的数据类型,只是声明了原有数据类型的一个别名。

####const常量
常量代表一个固定的值,在程序运行过程中不能够别改变,因此要求常量在定义时必须同时进行初始化。
使用规范 const int pi = 3.14;
使用const的好处:1、可以提高程序的可读性,2、可以提高程序的可维护性。

十进制数有正负之分,但是八进制和十六进制数只能表示无符号整数。
####指数形式表示法
使用规范:aEb的形式,代表数值a*10的b次方,b必须是十进制整数,a可以是十进制整数或者小数形式的实数。

单引号是字符的界定符,双引号是字符串的界定符。
在内存中,字符数据以ASCII码存储,所以字符数据和整形数据之间是可以相互转换的。

####变量在输出时,是根据其本身的数据类型进行输出的,由变量的数据类型决定如何解读内存中的数据。

字符串与字符不同,字符串在内存中除了存储所包含的字符外,还需要存储一个结束符‘\0’。

1、表达式
由运算符、操作数和分隔符号组成的序列,并且总能返回一个值作为表达式的结果。
表达式可以嵌套,例如:y=x=a+b;先将a和b相加,将结果赋给x,再将x=a+b的值赋给y。
2、语句和块
3、运算符
3.1、赋值运算符
带运算符的表达式一般也不能作为左值。
3.2、算数运算符
“/”除法,整数与整数相除,还是整数 5/2=2,只要有一个整数是浮点数,结果就是浮点数。
“%”取余,只能对整型数进行操作,余数的正负由被除数决定。
自增(++)与自减(--),又分前置与后置。

溢出是指运算结果超出了表达式的数据类型能够表示的范围,程序编译时不会发现溢出错误,运行时发生溢出也不一定给出错误信息。
发生溢出时,计算机按补码显示加法运算的结果。
3.3、关系运算符
由关系运算符将两个表达式连接起来,就是关系表达式,关系表达式的结果类型为bool,值只能为true或false,屏幕显示为1或0。
3.4、逻辑运算符
由逻辑运算符将简单的关系表达式连接起来,可构成复杂的逻辑表达式。逻辑表达式的结果类型也为bool,值只能为true或false,屏幕显示为1或0。
短路运算,只要能得出逻辑表达式的结果,没有进行的运算将不再继续。
3.5、位运算符
位运算的一个常见的用法就是实现掩码运算。
3.6、条件运算符
表达式1?(表达式2):(表达式3)
3.7、逗号运算符
表达式1,表达式2,……,表达式n;
3.8、数据类型转换
(1)隐式类型转换
转换的基本原则是将精度低的、范围较小的类型转换成精度高的、范围较大的类型。
对于同一精度的无符号数和有符号数,有符号数向着无符号数的方向进行隐式转换。
赋值运算符要求“=”左右的操作数据类型相同,如果不同,自动将右边的操作数类型向着左边的操作数类型转换。
(2)C风格强制类型转换
使用规范:(数据类型名)表达式、数据类型名(表达式)
如果强制将高精度的数据转成低精度的数据,数据精度将会受到损失,从这里来说,强制类型转换是一种不安全的类型转换。
(3)C++风格强制类型转换
static_cast<类型名>(表达式)
基本数据类型之间的强制转换都是允许的,但是有些数据类型的转换是不允许的(不同指针类型之间的转换)
遇到这种情况,使用C风格的编译不会报错,但是使用static_cast风格的在编译时就会报错,C++编程时建议使用static_cast。

使用putchar操作向显示器输出一个字符,将待输出的字符写在小括号里。
使用printf操作向显示器输出数据,将数据按照一定的格式输出。小括号里的内容为格式控制串和参数列表,用逗号分隔。
使用getchar操作从键盘读入一个字符。
使用scanf进行格式化的输入。
1、&是取地址运算符,这样才能把键盘输入的值保存到相应的变量里。
2、scanf的格式控制串里不要加多余的空格或者是‘\n’。
3、scanf("%d %f",&age,&weight);也可以写成scanf("%d,%f",&age,&weight);

iostream库中含有一个标准输入流对象cin和一个标准输出流对象cout,分别用来实现从键盘读入数据,将数据在屏幕上输出。
标准输入流cin负责从键盘读取数据,使用提取操作符“>>”,这些变量的数据类型不必一致。但是要求实际输入的数据与变量的类型一致,如果数据类型不匹配,则变量赋值不正确。例如:从键盘输入一个字符给整型变量,则赋值结果为一个随机数。
标准输出流对象cout负责将变量或常量中的数据输出到屏幕,使用插入操作符“<<”。
不能使用双等号判断两个浮点数是否相等,因为浮点数在计算机中的存储不是精确存储。
操作符sizeof(数据类型),用于确定某种数据类型的长度。

1、使用printf输出时,可以指定显示的宽度。
printf("number: %4d",3);
2、还可以使用%04d来控制宽度,当位数不足4位时,前面填0显示。
printf("number: %04d",3);
3、使用printf输出小数时,可以指定小数点后的位数。
printf("%.2f",3.1415926);

C++的I/O流类库对格式的控制,包含在头文件iomanip中。
1、setw(int n); 设置域宽
cout<<'s'<<setw(8)<<'d'<<setw(8)<<'e'<<endl;
setiosflags(ios:left)和setiosflags(ios:right),用来设置输出数据左对齐或右对齐,用cout输出一律默认是右对齐。
除了setw()操作符外,其他操作符一旦设定,对其后的所有输入/输出都产生影响,直到重新设置才改变格式。只有setw()操作符只对其后输出的第一个数据有效,对其他的数据没有影响。
setw()默认为setw(0),意思是按实际输出,如果输出的数值占用的宽度超过了setw(int n)设置的宽度,按实际宽度输出,不会损失精度。

int a=12345678;double b=123.456789;cout<<setw(5)<<a<<" " <<setw(5)<<b<<endl;
输出结果为12345678 123.456
整数a按照实际输出,实数b输出6位,实数默认的最大输出宽度为6位。

2、setfill(char c); 设置填充字符
使用规范:cout<<setfill(*)<<setw(5)<<'a'<<endl;
输出结果****a

3、setprecision(int n); 设置浮点数的数字个数
直接输出或者设置精度为0,都是输出6位有效数字。若设置其他精度,则按照设置的精度输出相应位数的有效数字。
setiosflags(ios:fixed)操作符是用定点方式表示浮点数,可以和setprecision(int n)结合使用,控制小数点右边的小数位数。
当设置为定点格式时,若不设置精度,则显示6位有效小数,故有效数字可以超过6位。
setiosflags(ios:scientific)操作符使用指数方式显示浮点数,以和setprecision(int n)结合使用,控制小数位数。
注:在使用操作符将小数截短显示时,将进行四舍五入处理。

如何进行字符串的处理?
1、使用字符数组或者字符指针
2、使用C++标准模板库(STL)中提供的string类型,string支持长度可变的字符串,第二种方法更方便、更安全。

头文件string
1、string对象的定义和初始化
string str1;
string str2("hello");
string str3=str2;
注:字符串常量会自动添加‘\0'作为结束符,而string类型不会。故字符串常量中hello为6个字符,string中hello为5个字符。
2、string对象的输入/输出
直接输入
3、string对象的赋值
直接使用赋值运算符“=”
4、string对象的连接
可以直接使用‘+’ ‘+=’
5、string对象的比较
直接比较大小,比较的是字符的ASCII码值
注:当长度不相同时,较短的string对象和较长的string对象前面的字符完全相同,较长的string对象大。

根据文件中数据的不同组织形式,一般把文件分为文本文件和二进制文件。
在文本文件中,每个字节存放着一个ASCII码,表示一个字符,比较直观,便于阅读。
二进制文件则是把内存中的数据按照它在内存中的存储形式原封不动地输出到磁盘文件中,二进制文件不能使用文本编辑软件查看。

头文件fstream
标准库的ifstream和ofstream用来定义文件流对象,ifstream提供文件的读操作,ofstream提供文件的写操作。
例:ofstream ofile("odata.txt");
定义了ofstream类的流对象ofile,并指定它和磁盘文件odata.txt关联,此后,ofile的使用与cout类似。

控制语句

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
任何程序都可以分解为3种基本控制结构:顺序结构、选择结构、循环结构。

程序的逻辑错误是指编译器检查不出来的错误,即编译时不出错,只在程序运行时才会变现为结果不正确。

switch(表达式){
case 常量表达式1: 语句1 break;
case 常量表达式2: 语句2 break;
case 常量表达式3: 语句3 break;
………
case 常量表达式4: 语句4 break;
default 语句n+1;
}

表达式的值可以是字符型、整型、枚举型,其它类型都是不允许的。
case常量表达式可以是字符型、整型、枚举型,各个case的值应该不同,先后顺序不影响执行结果。
break不是switch语句必需的,它的作用只是终止语句的执行,并跳出switch语句。当没有break时,会继续执行case语句,直到遇见break。
如果希望几个case共用一组执行语句,就可以不写break语句。
在switch语句中应该提供default情况,可以提醒编程者需要处理异常条件。
所有的case语句需要用{}包裹起来,case的语句块可以使用{}也可以不用,都可以。
switch语句通常比多分支if语句的执行效率高,可读性也更好。在编程时,应根据需要合理地进行选择。

循环结构的主要部分是:循环控制条件,循环体,循环控制变量。
在利用循环结构迭代求和时,表示和的变量的初值必须赋为0。

对于那些确定要执行一次或一次以上的程序,用while和do-while都是可以的,但是对于可能一次也不执行的语句,就必须使用while循环。

for循环:
for(循环控制变量赋初值;循环条件;修改循环控制变量值)
循环体语句

一般用for语句实现循环次数确定的问题,而用while和do-while语句实现循环次数事先不能确定的问题。
1、for语句的3个表达式中任意一个或几个可以不写,但是“;”不能省略。
2、省略表达式1,意味着赋值要在for语句之前完成。
3、省略表达式2,变成死循环,但是可以在内部使用break语句来结束循环。
4、省略表达式3,可以采用其它的方式控制循环变量。
5、同时省略表达式1和表达式3,此时for语句等同于while语句,直接采用while实现即可。
6、表达式1和表达式3都可以是逗号语句,for(sum=0,k=1;k<=10;k++) sum+=k*k;

break语句只用于循环结构的循环体或switch语句的各个分支中。
continue语句多用于循环结构的循环体中,与if-else语句配合使用,可实现满足一定条件时,提前结束本次循环。

随机数产生函数rand()可以用来生成一个0~RAND_MAX之间的随机数,但它并不是真正的随机数生成器,通常称为伪随机数。
所以在使用rand()之前,一般要先使用srand()函数为随机数序列设置种子。

要包含的头文件
#include<cstdlib>
#include<ctime>
用时间作为随机数种子
srand((unsigned)time(NULL));
随机产生整数0~5
rand()%6;

C++规定,从最内层开始,else总是与其前面最近的(未曾匹配的)if配对。
使用函数sqrt(x)需要包含数学库函数的头文件cmath

cin.get()一个字符一个字符地读取。

数组及自定义数据类型

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
1、数组的定义
用于表示具有一定顺序关系且类型相同的若干变量的集合,组成数组的变量称为该数组的元素。
使用规范:类型标识符 数组名[表达式]
int score[5]; 这样的语句将使系统给该数组分配一段能够存放5个整型变量的连续内存空间,数组名表示该内存空间的起始地址。

程序运行时,遇到数组定义语句就为这个数组分配一定数量的连续内存单元,数组元素依次占用这一连续内存空间,这段内存空间起始地址的外部标识就是数组名。数组名是一个地址常量,禁止给数组名赋值。
它所占用的字节数:sizeof(数组名)或N*sizeof(数组类型)

2、数组的初始化
2.1、在定义数组时,直接给出赋给数组元素的值,成为数组初始化。
2.2、类型标识符 数组名[常量表达式]={以逗号隔开的初始化值};
2.3、初始化数组时,给定的初始化数值不能比数组元素多,但可以比数组元素少,如果少,没有初始化的元素被初始化为0。
2.4、如果使用初始化列表,则表内至少包含一个初始值,否则编译时将会出现错误。只定义没有初始化各元素的值是随机值。
2.5、如果在初始化列表中给定数组元素的全部值,可以省略中括号中元素个数常量表达式。 int score[]={1,2,3,4,5};此时,编译器自动计算出数组元素的个数为5,给score数组分配能够存储5个int型数据的连续空间。

数组越界时,编译器在编译程序时不会检查这种错误。

3、字符数组
char型的数组称为字符数组,通常用来存储字符串,所以,可以用字符串常量给字符数组赋值。
char chArray[]="hello world!";
3.1、初始化字符数组
1、使用双引号内的字符串常量初始化字符数组 char array[10]={"hello"};可以省略大括号。char array[10]="hello";用这种方法初始化时,系统会在数组的最后一个元素后面自动补加一个‘\0’作为结束符。
2、用字符常量来初始化字符数组 char array[10]={'h','e','l','l','o'.'\0'};在这种方式下,编程者要自己添加字符串结束符(‘\0’),不要忘记给‘\0’留出空间。
3.2、字符数组的赋值
1、一个一个地赋值
2、使用C的库函数strcpy(字符数组1,字符串2);将字符串2复制到字符数组1中。此时,字符数组1要足够大,能容纳被复制的字符串。不能使用赋值语句将一个字符串常量或字符数组直接给字符数组赋值。

4、多维数组
以二维数组为例,一般遍历二维数组使用两层for循环比较合适,外层循环控制变量为数组元素的行下标,内层循环控制数组元素的列下标。

5、枚举类型
特点:只取有限种可能值。
用户可以自己定义一种数据类型,把这种数据类型的可能值一一列举出来,就可以使用这种数据类型来定义变量了。
使用规范:enum 新的数据类型的名称 {变量值列表}; enum weekday{sun,mon,tue,wed,thu,fri,sat};
枚举常量在机器内部仍然用整型数表示。
定义一个weekday类型的变量 weekday day;也可以在weekday前面写enum。 enum weekday day;
5.1、在类型定义以后,枚举元素按常量处理,不能对它们赋值。 sun=0是非法的
5.2、枚举元素有默认值,从0开始依次增加。
5.3、也可以在类型声明时另行指定枚举元素的值,没有指定的按照顺序递增。
5.4、枚举值可以进行关系运算,但不能进行其他运算。
5.5、枚举值可以直接赋给整型变量,但整数值不能直接赋给枚举变量,若要赋值,需要进行强制类型转换。
day = (weekday)3;

6、结构类型
组成结构的各个值可以具有不同的数据类型,而且每个值都有独立的名字。
使用规范:struct 结构类型名{
数据类型说明符1 成员名1;
数据类型说明符2 成员名2;
……
数据类型说明符n 成员名n;
};
这里定义了一种新的数据类型student,它的地位等价于int、float。

6.1、结构成员的访问
定义一个student类型的变量 student s1;
要访问结构的成员,需要使用圆点运算符“.”,引用形式为:结构变量名.成员名。
结构变量除了赋值运算外,不能整体进行操作,只能通过每个结构变量的成员的操作完成对结构变量的操作。
6.2、结构变量的初始化
1、在结构变量定义的同时设置初始值。 student s2={2014026,"lili","xi tu cheng"};
2、在程序中,单独给结构变量的各个成员赋值。 s1.num=2014026;strcpy(s1.name,"lili");
结构体除了可以由不同的基本数据类型的成员构成外,也可以包含已经定义的结构体类型的变量,即形成结构体定义的嵌套。
6.3、结构变量的赋值运算
1、属于同一结构类型的各个变量之间可以相互赋值。不能直接进行数组名的赋值,而结构类型的变量可以赋值。
2、不同结构的变量不允许相互赋值,即使这两个变量可能有相同的成员。

7、联合类型
有时需要使几个不同类型的变量共用同一组内存单元,这时可以声明一个联合数据类型。
使用规范:union 联合类型名{
数据类型说明符1 成员名1;
数据类型说明符2 成员名2;
……
数据类型说明符n 成员名n;
};
联合类型变量定义的语法形式:联合类型名 联合变量名;
在某时刻,只能使用多个成员的其中一个,联合成员的引用形式为:联合变量名.成员名
联合类型可以不声明名称,成为无名联合,常用作结构类型的内嵌成员。
7.1、同一段内存用来存放几种不同类型的成员,但是某一时刻只能存放其中一种,而不能同时存放几种。
7.2、联合变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后,原有的成员就会失去作用。
7.3、联合变量的地址和它的各个成员的地址是同一地址。
7.4、不能对联合变量名赋值,也不能在定义时初始化。
7.5、不能用联合变量作为函数参数或返回值。

库函数tolower()将大写字母转换成小写字母。
cin.getline(字符串的起始地址,最多输入字符的个数,行输入结束字符);
cin>>buffer[k];逐个字符输入。
cin>>buffer;接收键盘输入的整个字符串,遇到空格或回车结束。
cin.getline(buffer,80,'\n');能接收空格——视空格为字符,遇到回车或达到80个字符结束。
注:只有字符数组名能用cout输出数组里的数据,其他的使用循环输出。

在计算机内部处理时,把枚举类型按整型(int)对待。
结构类型所占存储空间,考虑到对齐的要求,系统会将所占空间不是4的倍数的成员空间调整为4的倍数。
对于结构和联合的成员,要将成员所占空间调整为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
67
68
69
70
71
72
73
74
75
C++中的模块以函数和类的形式实现,函数是具有一定功能又经常使用的相对独立的代码段。
函数由接口和函数体构成,函数的接口包括函数名、函数类型和形式参数表,函数体用于实现算法。
将程序中多处使用的、实现一定功能的特定代码段定义成函数,这样的函数称为自定义函数。在同一个程序中,一个函数只能定义一次。
主函数main()是程序执行的起点。
使用规范:返回值类型 函数名(形式参数表){
函数体(变量声明和语句)
}
函数名是这个独立代码段(函数体)的外部标识符,代表这个代码段在内存中的起始地址。
形式参数表从参数的类型、个数、排列顺序上规定了主调函数和被调函数之间信息交换的形式。
如果函数声明时没有写返回值类型,将默认为int类型。
C++语言不允许函数嵌套定义。
函数原型声明有可能使编译程序提前发现函数调用时参数类型不一致的错误,以免在程序运行时才发现这样的错误。

return语句使程序的执行流程从被调函数返回主调函数。
1、不返回值的形式
return;
2、返回值的形式
return 表达式;
注:不返回值的return语句只能用于void类型函数函数结束前的return语句也可以省略不写。

局部变量能够随其所在函数被调用而被分配内存空间,也随其所在函数调用结束而消失,提高了内存的利用率。
全局变量的作用范围是从它定义的位置开始至源文件结束,作用域是整个源文件。
全局变量在程序执行的整个过程中,始终位于全局数据内固定的内存单元。如果程序没有初始化全局变量,系统会将其初始化为0。

C++规定:内层标识符与外层标识符同名时,内层标识符可见,外层标识符不可见,内层变量屏蔽外层同名变量。
全局作用域运算符(::)
例如:在函数内部,(::pi使用的是全局的pi)
在同一个作用域中,不能声明同名的标识符。

编译预处理命令不是C++语句,所以都不使用“;”结束。

####递归函数
使用递归函数可以简洁明了地表达算法(算法的可读性好),但是算法的效率一般不高。
递归函数的函数体内有调用函数自身的语句或通过其他函数间接调用函数本身。

递归函数包含以下两个主要部分:
1、具有更简单参数的递归调用。
2、停止递归的终止条件(递归终止条件)。

####内联函数
函数调用时,系统首先要保存主调函数的相关信息,再将控制转入被调函数,这些操作增加了程序执行的时间开销。C++提供的内联函数形式可以减少函数调用的额外开销(时间、空间开销),一些常用的短小的函数适合采用内联函数形式。
使用规范:inline 函数类型 函数名(形式参数表){
函数体
}
形式上,只需要在函数类型前加一个关键字inline即可。内联函数是函数的一种特殊形式。

内联函数之所以能够减少函数调用时的系统空间和时间开销,是因为系统在编译程序时就已经把内联函数的函数体代码插入到相应的函数调用位置,成为主调函数内的一段代码,可以直接执行,不必再转换流程控制权。这样的结构自然节省了时间和空间开销,但使得主调函数代码变长。一般只把短小的函数写成内联函数。
注:1、内联函数体内不能包含循环语句、switch语句。
2、内联函数要先定义后调用,不能先声明函数原型,再调用、定义。

####重载函数
名称相同,形式参数的个数、类型、顺序不同的函数构成重载函数。
1、编译器是根据函数调用时实际参数的类型选择匹配的重载函数执行,严格匹配,否则的话看看能不能通过隐式转换找到一个匹配的调用。
2、重载函数常用于实现功能类似而所处理的数据类型不同的问题。
3、返回值类型不同不可以构成重载。
4、****ambiguous****,二义性,程序在执行时会出现歧义。

####带默认参数值的函数
1、调用具有默认参数值的函数时,如果提供实际参数值,则函数的形式参数值取自实际参数;如果不提供实际参数值,函数的形式参数采用默认参数值。
2、默认参数值函数如果有多个参数,而其中只有部分参数有默认值,则这些具有默认值的参数值应该位于形式参数表的最右端。或者说,形式参数表中具有默认参数值的参数右边不能出现没有默认值的参数。
3、如果默认参数值函数是先声明、后定义的,则在声明函数原型时就指定默认参数值。如果定义在先,则在函数定义的形式参数表中指定默认值。

C++程序中使用的变量可分为4种存储类型:auto、register、extern、static。
1、静态变量分为静态局部变量和静态全局变量,静态变量在程序运行期间一直在静态存储区占有固定的存储空间。
2、静态局部变量在定义时初始化一次,如果没有初始化,系统将其初始化为0。其间其值一直保持,直到下一次调用时被修改。
3、静态全局变量只能在其定义的文件中使用,不能被多文件程序结构中的其他文件访问。

一个变量在内存中存在的时间为变量的生存期。

C++语言的函数参数传递方式有两种:
1、值传递
2、地址传递

函数调用时值传递由实参到形参是单向的。
如果函数调用时,实参使用地址变量或者地址常量,也就是传给形式参数的是地址值,则参数传递方式为地址传递。

指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
存放地址的变量称为指针变量,简称为指针。
在访问指针变量时,只能看到地址,只有通过这个地址,才能访问地址单元中的内容,这样的访问称为对于内存单元的间接访问。
指针变量定义的格式:<类型名> *变量名1,*变量名2;
指针变量尽管存放的都是地址,但是存在类型上的差别。指针变量的类型就是它所指定的地址单元中存放的数据的类型。
指针变量在声明后,变量的值(地址)是随机的,这样的指针是不安全的,给指针赋为0或者写成NULL,表示空指针,不指向任何内存单元。
指针变量必须初始化后才可以正确使用,初始化就是给它分配一个有效的数据地址。
1、在定义时初始化,char ch1='Y'; char *pch1=&ch1;
2、定义指针后,用赋值的方式初始化, char ch1='Y'; char *pch1=NULL; pch1=&ch1;
间接引用运算符“*”是一种一元运算符,它和指针变量连用,对指针所指向的内存单元进行间接访问。使用格式:*指针变量
指针可以进行加减法,不过和一般的算数运算有不同的含义。指针的加减法表示当前位置前后第n个数据的地址。

cout<<"kl["<<i<<"]"; 输出方式

指针和指针直接相加是没有意义的,也是不允许的,指针和指针相减是可以的,其意义是求出两个指针之间可以存放几个指定类型的数据。
1、不允许用一个整数减一个指针。
2、指针的赋值运算一定是地址的赋值。
3、不同类型的指针是不可以相互赋值的。在为指针赋值时,不存在类型的自动转换机制。
4、两个指针变量相等,表示它们指向相同的内存地址。

动态内存是程序执行时才可以申请、使用和释放的内存,动态内存也称为堆内存。
动态内存不能通过变量名来使用,而只能通过指针来使用。

####C语言动态内存的申请与释放
int *pn=(int *)malloc(sizeof(int));
free(*pn);

####C++动态内存的申请与释放
int *pn=new int(10); 如果申请成功,指针pn就获得一个有效的地址,并且使得*pi=10;
delete<*pn>;
申请动态一维数组时,要在new表达式中加上申请数组的大小,其格式为: new<类型名>[表达式];
delete[]<指针名>;

引用是C++中新引入的概念,是C语言中不存在的数据类型。
引用是变量或者其他编程实体(如对象)的别名,因此,引用是不可以单独定义的。
指针变量本身也有自己的地址,是可以独立存在的,而引用是不可以独立存在的。

使用规范:<类型名>&引用名=变量名; 变量名必须是已经定义的,并且和引用的类型相同。
引用必须在声明的同时完成初始化,不可以先声明引用,再用另一个语句对它进行初始化。
注:1、引用不能独立存在,它只是其他变量的别名。
2、引用必须在声明的同时初始化。
3、引用一旦定义,引用关系就不可以更改。如果B是A的引用,就不可能是其他变量的引用。
4、引用的类型就是相关变量的类型,引用的使用和变量的使用相同。
在实际的程序中,没有必要在一个程序作用域中定义一个变量后,又定义它的引用。在程序中真正使用引用的地方是在函数的调用中;或者将引用作为函数的形式参数,或者将引用作为函数的返回值。

用指针作为函数参数实现地址调用,需要满足的条件:
1、函数的形式参数是指针变量。
2、函数的实际参数是内存的地址,具体可以说是数组名、变量的地址、用变量地址初始化的指针。
3、形式参数指针类型和实际参数地址类型必须相同。

******如果需要通过函数调用改变主调函数中变量的值,必须采用地址调用方式。
******直接的值交换可以改变函数中的交换,但是返回的主调函数中没有完成交换。
使用地址交换示例:
void change(int *x , int *y){
int temp = *x;
*x = *y;
*y = temp;
}
int a , b;
change(&a , &b);

引用作为函数参数
引用的主要作用就是作为函数的形式参数。
特点:1、引用作为形式参数时,实际参数是相同类型的变量。
2、引用作为形式参数时,参数传递属于地址传递。
3、引用作为形式参数时,在函数中并不产生实际参数的副本,形式参数的引用和实际参数的变量实际上是同一个实体。
4、函数对引用的操作,也是对实参变量的操作,函数调用可以改变实际参数的值。
void change(int &a , int &b){
int j =a;
a=b;
b=j;
}
int a , b;
change(a,b);

两者相较,都是地址调用,都不建立实参的副本,不过指针需要定义指针变量,而引用不用,故而执行效率更高。

1、常指针
常指针就是指向常量的指针。就是规定指针所指向的内容```不可以通过指针的间接引用来改变```,通过其它的方式还是可以改变的。
常指针定义的格式 const <类型名> *<指针名>;
例如:const int *ptint; 指针ptint的类型是(const int *);
2、常引用
常引用使用规范:const <类型名><引用名>&;
3、指针常量
指针常量。也就是指针本身的内容是常量,不可以改变。
使用规范:<类型名>*const<指针名>=<初值>;
数组名就是一个指针常量。
4、指针函数
如果一个函数的返回值是指针,则这样的函数称为指针函数。
例:int *func(int k); 函数返回一个整形数据的指针。返回指针,实际上就是返回一个内存单元的地址。
5、函数指针
指向函数的指针称为函数指针。
使用规范:<类型名>(*指针名)(形参列表);
例:int (*fptr)(int , int);
6、字符串
6.1、C++字符串常量是用双引号括起的字符序列,并以字符'\0'作为结束标志。
6.2、字符串常量存放在内存的常量区域,有自己固定的首地址,这个地址可以看作常指针也可以看作指针常量。
6.3、C++处理字符串有两种方式:数组方式和指针方式
数组方式是将字符串存入字符数组后,再进行处理。 char string_array[]="what is a nice day!";
指针方式是用字符串常量来初始化一个字符指针。 char *string_pt="what is a nice day!";
6.4、字符串处理函数,头文件<cstring>
功能 函数原型 返回值 说明
字符串长度 int strlen(const char *string); 长度值 ‘\0’不计入
字符串复制 char *strcpy(char *s1,const char *s2); 复制的字符串 s1要有足够的空间
按字符数复制 char *strncpy(char *s1,const char *s2,int n); 复制的字符串 s1要有足够的空间
字符串比较 int strcmp(const char *s1,const char *s2); >0对应s1>s2,类似 按字符顺序的ASCII码值比较
字符串连接 char *strcat(char *s1,const char *s2); 连接后的字符串 s1要有足够的空间
注:strncpy注意给‘\0’留出位置

####访问数组的方式及注意事项
int A[10],*pa = A
1、数组名和下标 A[0],A[4]
2、指针和下标 pa[0],pa[4]
3、指针加偏移量的间接引用 *(pa+0),*(pa+4)
4、数组名加偏移量的间接引用 *(A+0),*(A+4)
5、指针自加后的间接引用 *pa++ 但是采用这种方式会改变指针本身的值 必要时要对指针重新初始化
注:不允许数组名自加后的间接引用来访问数组元素 *A++ 因为数组名是常数,不可以通过加1进行改变

指针数组
若数组元素是某种类型的指针,称这样的数组为指针数组
使用规范:<类型> *<数组名>[常量表达式]
char *member_name[10]={"Merry","John","Hill"};
访问二维数组的方式:*(*(指针名+i)+j);

####指针与结构体
1、struct student{
long num;
char name[20];
float score;
};
student stu={20041118,"Lili",81};
student *ps = &stu;

2、使用new操作在堆中给结构指针分配空间
student *ps = new student;

用结构指针访问结构成员时,用箭头操作符替代原来的点操作符

####内存泄漏和指针悬挂
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
1、目前的程序设计主要分为两类:面向过程的程序设计、面向对象的程序设计。
2、在程序设计语言中,一个类是一种新的数据类型。
3、类和结构体都是一种自定义数据类型,但是类的成员默认是private的,不可以任意访问,更重要的是类和对象具有继承和多态的特性。
4、定义类时,计算机内部并没有实质性的操作,所以也称为类的声明。
使用规范:
class 类名称{
publc:
公有成员
protected:
保护型成员
private:
私有成员
}
注:“成员”可以是数据成员,也可以是成员函数的原型。关键字public、private、protected说明类成员的访问控制属性。

1、类的成员包括数据成员和函数成员,分别描述问题的属性和操作,是不可分割的两个方面。数据成员的声明方式与一般变量相同;函数成员用于描述类的对象可以进行的操作,一般在类中声明原型,在类声明之后定义函数的具体实现。
2.1、私有成员只允许本类的成员函数访问
2.2、公有成员是类对外的接口,在类声明和类实现之后,类的对象可以访问公有成员
2.3、保护型成员的可访问性和私有成员的性质相似,差别在于继承过程中派生类的影响不同
2.4、一个成员只能有一种访问控制属性
2.5、一般情况下,一个类的数据成员应该声明为私有成员,这样封装性较好;公有函数用于对外的接口,用于对类的访问。

3、在类声明之后定义成员函数的语法形式:
返回值类型 类名::成员函数名(参数表){
函数体
}
注:通过类名和作用域操作符“::”表示函数属于哪个类

4、类的成员函数
4.1、带默认参数值的成员函数
类的成员函数可以有默认形参值,其调用规则与普通函数相同,默认值要写在函数原型声明中,函数实现时不写默认值。
4.2、内联成员函数
内联成员函数的声明有两种方式:隐式声明、显式声明
在类声明时定义的成员函数都是内联函数,函数定义时没有任何的附加说明,所以称为隐式说明。
在类声明之后定义的内联函数需要在函数头部用关键字inline。
使用规范:inline 返回值类型 类名::成员函数名(参数表){
函数体
}
4.3、成员函数的重载
成员函数可以像普通函数那样进行重载,类名是成员函数名的一部分,所以一个类的成员函数与另一个类的成员函数即使同名,也不能认为是重载。

5、定义一个对象时要为其分配存储空间,一个对象所占用的内存空间是类的数据成员占用的空间总和,类的成员函数存放在代码区,不占用内存空间。
在类的外部,只能通过对象访问类的公有成员;在类的成员函数内部,可以直接访问类的所有成员,这就实现了对访问范围的有效控制。

6、因为不同类型对象的初始化和清除工作是不一样的,因此构造函数和析构函数都是从属于某个类的,即每个类都要定义它自己的构造函数和析构函数,它们是类的成员函数。

6.1、构造函数的定义
——————>构造函数用来完成对象的初始化,给对象的成员函数赋初值。
定义构造函数的一般形式:
class 类名{
public:
类名(形参表); //构造函数的原型
类的其他成员
};
类名::类名(形参表){
//函数体
}
6.1.1、在创建对象时,系统会自动调用构造函数创建对象并做一些初始化工作。
6.1.2、构造函数的特点:构造函数的名称与类名相同,构造函数没有返回值,构造函数一定是公有函数。
6.1.3、作为类的成员函数,构造函数可以直接访问类的所有数据成员。
6.1.4、在类的内部定义的构造函数是内联函数,构造函数可以带默认形参值,也可以重载。

6.2、构造函数的重载
构造函数可以像普通函数一样重载,调用时根据参数的不同,选择其中合适的一个,但是会繁琐。可替代的方法是:使用带默认参数值的构造函数。

6.3、带默认参数值的构造函数
函数可以为其参数设置默认值,构造函数也可以。

6.4、默认构造函数和无参构造函数
没有定义构造函数时,系统在编译时会自动生成一个默认形式的构造函数。
形式:类名::类名(){}
这是一个既没有形式参数,又没有任何语句的函数,不能为对象的初始化做任何事情。
只有在类中没有定义任何构造函数的情况下,才能使用默认的构造函数。
类中定义了构造函数,编译时系统不会再自动生成一个默认构造函数。

6.5、析构函数
对象占用的空间要通过析构函数来释放,析构函数的原型:~类名()
如果程序不定义析构函数,系统也会提供一个默认的析构函数:~类名(){}
这个析构函数只能用来释放对象的数据成员所占用的空间,但不包括堆内存空间。

####构造函数和析构函数都没有返回值
####构造函数可以重载,析构函数不可以重载
####析构函数没有形式参数,是在对象生存期即将结束时由系统自动调用的,如果没有定义析构函数,会自动生成一个默认析构函数。

定义时要调用构造函数进行初始化,程序运行结束时,栈空间里面的对象要进行析构,程序会自动调用析构函数,析构和构造的顺序相反。

堆对象
声明形式:类名* 对象指针名
使用对象指针访问对象的成员,要使用“->”运算符:对象指针名——>公有成员
例:
void main(){
Clock* pmyclock = new clock;
pmyclock->SetTime(12,5,0);
pmyclock->ShowTime();
delete pmtclock;
}

this指针
this指针指出了成员函数当前所操作的数据所属的对象。不同对象调用成员函数时,this指针将指向不同的对象,也就可以访问不同的数据成员。

继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1、类的继承是在现有类的基础上创建新类,并扩展现有类的功能的机制,称现有的类为基类,新建的类为派生类。
2、若派生类只有一个直接基类,则称这种继承方式为单继承;若派生类有多个直接基类,则称为多继承。
2.1、派生类继承了基类的所有成员,派生类对象包含基类的数据成员,也可以直接调用基类公有函数。
2.2、派生类对象不可以直接访问基类的私有成员。
2.3、派生类对象可以通过基类的公有函数访问基类的私有成员。
2.4、派生类继承了基类的所有成员,但不包括构造函数、析构函数和默认赋值运算符。
3、继承方式和访问控制
派生类访问基类成员时,可以访问public成员而不能访问private成员。
3.1、protected属性
派生类可以访问基类的一些成员,但是仍禁止用户代码访问这些成员,可以将这些成员设置为protected访问方式。
类的对象不能访问protected属性的成员,但是,派生类的成员函数可以访问基类的protected属性的成员。
3.2、继承方式影响访问控制
3.2.1、无论是哪一种继承方式,派生类成员函数都可以访问基类的public成员和protected成员,但都不能访问基类的private成员。
3.2.2、只有public继承的派生类对象可以访问基类的public成员,protected和private继承的派生类对象不能访问基类public成员。
3.2.3、对于public继承,基类的public、protected成员在派生类中仍将保持原来的访问属性。
3.2.4、对于protected继承,基类的public成员、protected成员在派生类中都变为protected属性。
3.2.5、对于private继承,基类的public成员、protected成员在派生类中都变为private属性。
3.2.6、不论是哪一种继承方式,基类的private成员在派生类中都不可被访问。
4、定义虚基类是为了消除继承时的二义性问题。

异常处理

1
2
3
4
5
6
7
8
9
C++异常处理的语法表述如下:
try{
受保护语句;
throw异常;
其他语句;
}
catch(异常类型){
异常处理语句;
}

课外

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1、
头文件#include<time.h>
获取当前时间的代码:
time_t t = time(0);
char temp[64];
strftime(temp , sizeof(temp) , "%Y/%m/%d %X %A" , localtime(&t));

2、
带有默认参数的函数
2.1、默认参数的顺序,从右向左,不能跳跃。
2.2、定义在前,调用在后,默认参数在定义处。声明在前,调用在后,默认参数在声明处。
2.3、一个函数不能既做重载,又做默认参数的函数,会引起二义性。

3、
引用的本质是指针,对指针的包装。