一、 C++基础
关键字
▪ alignas
▪ constexpr
▪ char16_t 和 char32_t
▪ decltype
▪ nullptr
▪ noexcept
▪ static_assert
▪ auto
▪ asm
▪ auto
▪ *_cast
▪ bool,true,false
▪ break,continue,goto
▪ case,default,switch
▪ catch,throw,try
▪ char,wchar_t
▪ const,volatile
▪ struct,class,union
▪ delete,new
▪ do,for,while
▪ if,else
▪ enum
▪ explicit
▪ export
▪ extern
▪ friend
▪ inline
▪ mutable
▪ namespace
▪ operator
▪ private,protected,public
▪ register
▪ return
▪ static
▪ sizeof
▪ template
▪ this
▪ typedef
▪ virtual
▪ typeid
▪ typename
▪ using
▪ void
▪ try
变量
根据变量定义的位置,可以把变量分成全局变量(global variable)与局部变量(local variable)。
全局变量是定义在函数体外部的变量,它能被所有函数使用。局部变量是指定义在函数或复合语句中定义的变量,只能在函数或复合语句中使用。
C++中变量有auto 、extern、register、static四种存储类型。
1.auto说明符
auto说明符说明定义的是一个局部变量。函数中的局部变量,如果不用关键字static加以声明,编译系统对它们是动态分配存储空间的。局部变量默认存储类型为auto,所以在程序中很少使用auto说明符说明。
2. register说明符
为提高执行效率,C++允许将局部变量的值存放在CPU的寄存器中,需要
时直接从寄存器中取出参加运算,不必到内存中去存取,这种类型的局部变量称为寄存器变量,用关键字register声明。在程序中定义寄存器变量对编译系统只是建议性的,而不是强制性的。
3. extern说明符
如果一个完整的计算机程序很大,分成多个模块,放在不同的文件中,分开编译成目标文件,最后连接成一个完整的可执行代码。对于所有模块共同使用的全局变量,如果在所有的模块中都定义,在连接时就会出错。
解决办法是只在一个模块中定义全局变量,在其他模块中用extern说明这是一个“外来”的全局变量。
4. static说明符
static说明符用于在定义变量时将变量声明为成static(静态)变量。
其格式为:
static 数据类型 变量名=初值;
static可用来声明全局静态变量和局部静态变量。当声明全局静态变量时,全局静态变量只能供本模块使用,不能被其它模块再声明为extern变量.
常量
1. 整数常量
整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制。
整数常量也可以带一个后缀,后缀是 U 和 L 的组合,U 表示无符号整数(unsigned),L 表示长整数(long)。后缀可以是大写,也可以是小写,U 和 L 的顺序任意。
下面列举几个整数常量的实例:
212 // 合法的
215u // 合法的
0xFeeL // 合法的
以下是各种类型的整数常量的实例:
85 // 十进制
0213 // 八进制
0x4b // 十六进制
30 // 整数
30u // 无符号整数
30l // 长整数
30ul // 无符号长整数
2. 浮点常量
浮点常量由整数部分、小数点、小数部分和指数部分组成。您可以使用小数形式或者指数形式来表示浮点常量。
当使用小数形式表示时,必须包含整数部分、小数部分,或同时包含两者。当使用指数形式表示时, 必须包含小数点、指数,或同时包含两者。带符号的指数是用 e 或 E 引入的。
下面列举几个浮点常量的实例:
3.14159 // 合法的
314159E-5L // 合法的
3. 布尔常量
布尔常量共有两个,它们都是标准的 C++ 关键字:true 值代表真 false 值代表假。
4. 字符常量
字符常量是括在单引号中。如果常量以 L(仅当大写时)开头,则表示它是一个宽字符常量(例如 L’x’),此时它必须存储在 wchar_t 类型的变量中。否则,它就是一个窄字符常量(例如 ‘x’),此时它可以存储在 char 类型的简单变量中。
字符常量可以是一个普通的字符(例如 ‘x’)、一个转义序列(例如 ‘\t’),或一个通用的字符(例如 ‘\u02C0’)。
在 C++ 中,有一些特定的字符,当它们前面有反斜杠时,它们就具有特殊的含义,被用来表示如换行符(\n)或制表符(\t)等。下表列出了一些这样的转义序列码:
转义序列 含义
\ \ 字符
\’ ’ 字符
\” ” 字符
\? ? 字符
\a 警报铃声
\b 退格键
\f 换页符
\n 换行符
\r 回车
\t 水平制表符
\v 垂直制表符
\ooo 一到三位的八进制数
\xhh … 一个或多个数字的十六进制数
- 字符串常量
字符串字面值或常量是括在双引号 “” 中的。一个字符串包含类似于字符常量的字符:普通的字符、转义序列和通用的字符。
您可以使用空格做分隔符,把一个很长的字符串常量进行分行。
下面的实例显示了一些字符串常量。下面这三种形式所显示的字符串是相同的。
“hello, dear”
“hello, \
dear”
“hello, ” “d” “ear” - 定义常量
在 C++ 中,有两种简单的定义常量的方式:
使用 #define 预处理器。
使用 const 关键字。
#define 预处理器
下面是使用 #define 预处理器定义常量的形式:
#define identifier value
- const 关键字
您可以使用 const 前缀声明指定类型的常量,如下所示:、const type variable = value;
数据类型
1. bool 布尔型
2. char 字符型 8位
3. wchar_t 宽字符型 16位
4. short 短整型 16位
5. int 整形 16位
6. long 长整形 32位
7. float 单精度浮点型 6为有效数字
8. double 双精度浮点型 10位有效数字
9. long double 扩展精度浮点型 10位有效数字
运算符
优先级 名称 运算符 可重载性
所需变量个数 结合性
1 作用域运算符 :: 否 自左向右
2 成员访问运算符 . 否 双目运算符 自左向右
指向成员运算符 ->
下标运算符 [ ]
括号 / 函数运算符 ()
3 自增运算符 ++ 单目运算符 自右向左
自减运算符 –
按位取反运算符 ~
逻辑非运算符 !
正号 +
负号 -
取地址运算符 &
地址访问运算符 *
强制类型转换运算符 (Type)
类型长度运算符 sizeof()
否
内存分配运算符 new
取消分配内存运算符 delete
类型转换运算符 castname_cast 否
4 成员指针运算符 .* 否 双目运算符 自左向右
->*
5 乘号 * 双目运算符 自左向右
除号 /
取余运算符 %
6 加号 + 双目运算符 自左向右
减号 -
7 位左移运算符 << 双目运算符 自左向右
位右移运算符 >>
8 小于号 < 双目运算符 自左向右
小于等于号 <=
大于号 >
大于等于号 >=
9 等于号(判等运算符) == 双目运算符 自左向右
不等于号 !=
10 按位与 & 双目运算符 自左向右
11 按位异或 ^ 双目运算符 自左向右
12 按位或
| 双目运算符 自左向右
13 逻辑且 && 双目运算符 自左向右
14 逻辑或 || 双目运算符 自左向右
15 条件运算符 ? : 否 三目运算符 自右向左
16 赋值运算符 = 双目运算符 自右向左
复合赋值运算符 +=
-=
*=
/=
%=
<<= 、>>=
&=
|=
^=
17 抛出异常运算符 throw 否 自左向右
18 逗号运算符 ,
表达式
一、算术运算符
A.算术操作符 +、-、* 和 / 具有直观的含义:加法、减法、乘法和除法。对两个整数做除法,结果仍为整数,如果它的商包含小数部分,则小数部分会被截除.
B.操作符 % 称为“求余(remainder)”或“求模(modulus)”操作符,用于计算左操作数除以右操作数的余数。该操作符的操作数只能为整型,包括 bool、char、short 、int 和 long 类型,以及对应的 unsigned 类型.
C.操作符 %或操作符/ ,如果只有一个操作数为负数,这两种操作的结果取决于机器;求模结果的符号也取决于机器,而除法操作的值则是负数(或零)。
二、关系操作符和逻辑操作符:
A.逻辑与和逻辑或操作符总是先计算其左操作数,然后再计算其右操作数。只有在仅靠左操作数的值无法确定该逻辑表达式的结果时,才会求解其右操作数。我们常常称这种求值策略为“短路求值(short-circuit evaluation)”。对于逻辑与操作符,一个很有价值的用法是:如果某边界条件使 expr2 的计算变得危险,则应在该条件出现之前,先让 expr1 的计算结果为 false。例如,编写程序使用一个 string 类型的对象存储一个句子,然后将该句子的第一个单词的各字符全部变成大写,可如下实现:
string s("Expressions in C++ are composed...");
string::iterator it = s.begin();
// convert first word in s to uppercase
while (it != s.end() && !isspace(*it)) {
*it = toupper(*it); // toupper covered in section 3.2.4 (p. 88)
++it;
}
B. bool 类型可转换为任何算术类型——bool 值 false 用 0 表示,而 true 则为 1。
三、位操作符
A.位操作符使用整型的操作数。位操作符将其整型操作数视为二进制位的集合,为每一位提供检验和设置的功能。
B.位操作符操纵的整数的类型可以是有符号的也可以是无符号的。如果操作数为负数,则位操作符如何处理其操作数的符号位依赖于机器。于是它们的应用可能不同:在一个应用环境中实现的程序可能无法用于另一应用环境。对于位操作符,由于系统不能确保如何处理其操作数的符号位,所以强烈建议使用unsigned整型操作数。
C. << 和 >> 操作符提供移位操作,其右操作数标志要移动的位数。这两种操作符将其左操作数的各个位向左(<<)或向右(>>)移动若干个位(移动的位数由其右操作数指定),从而产生新的值,并丢弃移出去的位。左移操作符(<<)在右边插入 0 以补充空位。对于右移操作符(>>),如果其操作数是无符号数,则从左边开始插入 0;如果操作数是有符号数,则插入符号位的副本或者 0 值,如何选择需依据具体的实现而定。移位操作的右操作数不可以是负数,而且必须是严格小于左操作数位数的值。否则,操作的效果未定义。
D.位与操作(&)需要两个整型操作数,在每个位的位置,如果两个操作数对应的位都为 1,则操作结果中该位为 1,否则为 0。位异或(互斥或,exclusive or)操作符(^)也需要两个整型操作数。在每个位的位置,如果两个操作数对应的位只有一个(不是两个)为 1,则操作结果中该位为 1,否则为 0。位或(包含或,inclusive or)操作符(|)需要两个整型操作数。在每个位的位置,如果两个操作数对应的位有一个或者两个都为 1,则操作结果中该位为 1,否则为 0。
E.移位操作符具有中等优先级:其优先级比算术操作符低,但比关系操作符、赋值操作符和条件操作符优先级高。若 IO 表达式的操作数包含了比IO操作符优先级低的操作符,相关的优先级别将影响书写该表达式的方式。通常需使用圆括号强制先实现右结合:
cout << 42 + 10; // ok, + has higher precedence, so the sum is printed
cout << (10 < 42); // ok: parentheses force intended grouping; prints 1
cout << 10 < 42; // error: attempt to compare cout to 42!
四、赋值运算符:赋值操作的右结合性,只要被赋值的每个操作数都具有相同的通用类型,C++语言允许将这多个赋值操作写在一个表达式中。
五、条件操作符:
cond ? expr1 : expr2;
其中,cond 是一个条件判断表达式,条件操作符首先计算 cond 的值,如果 cond 的值为 0,则条件为 false;如果 cond 非 0,则条件为 true。无论如何,cond 总是要被计算的。然后,条件为 true 时计算 expr1 ,否则计算 expr2 。和逻辑与、逻辑或(&& 和 ||)操作符一样,条件操作符保证了上述操作数的求解次序。expr1 和 expr2 中只有一个表达式被计算。
六、sizeof 操作符:
A、sizeof 操作符的作用是返回一个对象或类型名的长度,返回值的类型为 size_t,长度的单位是字节,size_t表达式的结果是编译时常量,该操作符有以下三种语法形式:
sizeof (type name);
sizeof (expr);
sizeof expr;
B.将 sizeof 应用在表达式 expr 上,将获得该表达式的结果的类型长度
C.将 sizeof 用于 expr 时,并没有计算表达式 expr 的值。特别是在 sizeof *p 中,指针 p 可以持有一个无效地址,因为不需要对 p 做解引用操作。
D.使用 sizeof 的结果部分地依赖所涉及的类型:
1.对 char 类型或值为 char 类型的表达式做 sizeof 操作保证得 1。
2.对引用类型做 sizeof 操作将返回存放此引用类型对象所需的内在空间大小。
string p; string &pr=p;
cout<<sizeof pr; //将输出string类型的大小
3.对指针做 sizeof 操作将返回存放指针所需的内在大小;注意,如果要获取该指针所指向对象的大小,则必须对指针进行解引用。
4.对数组做 sizeof 操作等效于将对其元素类型做 sizeof 操作的结果乘上数组元素的个数。
E.逗号表达式是一组由逗号分隔的表达式,这些表达式从左向右计算。逗号表达式的结果是其最右边表达式的值。如果最右边的操作数是左值,则逗号表达式的值也是左值。
七、求值顺序:C++中,规定了操作数计算顺序的操作符还有条件(?:)和逗号操作符。除此之外,其他操作符并未指定其操作数的求值顺序。如果一个子表达式修改了另一个子表达式的操作数,则操作数的求解次序就变得相当重要:
// oops! language does not define order of evaluation
if (ia[index++] < ia[index])
然而,C++ 语言不能确保从左到右的计算次序。事实上,这类表达式的行为没有明确定义。 一个表达式里,不要在两个或更多的子表达式中对同一对象做自增或自减操作。
八、new 和 delete 表达式
A、动态创建对象:定义变量时,必须指定其数据类型和名字。而动态创建对象时,只需指定其数据类型,而不必为该对象命名。取而代之的是,new 表达式返回指向新创建对象的指针,我们通过该指针来访问此对象:
int i; // named, uninitialized int variable
int *pi = new int; // pi points to dynamically allocated,
// unnamed, uninitialized int
这个 new 表达式在自由存储区中分配创建了一个整型对象,并返回此对象的地址,并用该地址初始化指针 pi。
B、动态创建对象的初始化:动态创建的对象可用初始化变量的方式实现初始化:
int i(1024); // value of i is 1024
int *pi = new int(1024); // object to which pi points is 1024
string s(10, '9'); // value of s is "9999999999"
string *ps = new string(10, '9'); // *ps is "9999999999"
C、动态创建对象的默认初始化:如果不提供显式初始化,动态创建的对象与在函数内定义的变量初始化方式相同。对于类类型的对象,用该类的默认构造函数初始化;而内置类型的对象则无初始化。
注意new后面的类型没有括号
string *ps = new string; // initialized to empty string
int *pi = new int;
D.通过在类型名后面使用一对内容为空的圆括号对动态创建的对象做值初始化。内容为空的圆括号表示虽然要做初始化,但实际上并未提供特定的初值。对于提供了默认构造函数的类类型(例如 string),没有必要对其对象进行值初始化:无论程序是明确地不初始化还是要求进行值初始化,都会自动调用其默认构造函数初始化该对象。而对于内置类型或没有定义默认构造函数的类型,采用不同初始化方式则有显著的差别:
int *pi = new int; // pi points to an uninitialized int
int *pi = new int(); // pi points to an int value-initialized to 0
E.撤销动态创建的对象:动态创建的对象用完后,程序员必须显式地将该对象占用的内存返回给自由存储区。C++ 提供了 delete 表达式释放指针所指向的地址空间。
delete pi;
如果指针指向不是用 new 分配的内存地址,则在该指针上使用 delete 是不合法的。
F.零值指针的删除:如果指针的值为 0,则在其上做 delete 操作是合法的,但这样做没有任何意义.C++ 保证:删除 0 值的指针是安全的。
G.在 delete 之后,重设指针的值:删除指针后,该指针变成悬垂指针。悬垂指针指向曾经存放对象的内存,但该对象已经不再存在了。悬垂指针往往导致程序错误,而且很难检测出来。一旦删除了指针所指向的对象,立即将指针置为 0,这样就非常清楚地表明指针不再指向任何对象。
H.下面三种常见的程序错误都与动态内存分配相关:
删除( delete )指向动态分配内存的指针失败,因而无法将该块内存返还给自由存储区。删除动态分配内存失败称为“内存泄漏(memory leak)”。内存泄漏很难发现,一般需等应用程序运行了一段时间后,耗尽了所有内存空间时,内存泄漏才会显露出来。读写已删除的对象。如果删除指针所指向的对象之后,将指针置为 0 值,则比较容易检测出这类错误。对同一个内存空间使用两次 delete 表达式。当两个指针指向同一个动态创建的对象,删除时就会发生错误。如果在其中一个指针上做 delete 运算,将该对象的内存空间返还给自由存储区,然后接着 delete 第二个指针,此时则自由存储区可能会被破坏
九、类型转换
如果两个类型之间可以相互转换,则称这两个类型相关。
语句
auto :声明自动变量 一般不使用
double :声明双精度变量或函数
int: 声明整型变量或函数
struct:声明结构体变量或函数
break:跳出当前循环
else :条件语句否定分支(与 if 连用)
long :声明长整型变量或函数
switch :用于开关语句
case:开关语句分支
enum :声明枚举类型
register:声明积存器变量
typedef:用以给数据类型取别名(当然还有其他作用)
char :声明字符型变量或函数
extern:声明变量是在其他文件正声明(也可以看做是引用变量)
return :子程序返回语句(可以带参数,也看不带参数)
union:声明联合数据类型
const :声明只读变量
float:声明浮点型变量或函数
short :声明短整型变量或函数
unsigned:声明无符号类型变量或函数
continue:结束当前循环,开始下一轮循环
for:一种循环语句(可意会不可言传)
signed:生命有符号类型变量或函数
void :声明函数无返回值或无参数,声明无类型指针(基本上就这三个作用)
default:开关语句中的“其他”分支
goto:无条件跳转语句
sizeof:计算数据类型长度
volatile:说明变量在程序执行中可被隐含地改变
do :循环语句的循环体
while :循环语句的循环条件
static :声明静态变量
if:条件语句
二、面向对象语言特点
类的定义
结构体定义格式:struct 结构体名 {
类型名 成员名1;
类型名 成员名2;
……
类型名 成员名n;
};
成员函数
成员函数
作为类成员的成员函数描述了对类中的数据成员实施的操作。成员函数的定义、声明格式与非成员函数(全局函数)的格式相同。成员函数可以放在类中定义,也可以放在类外。放在类中定义的成员函数为内联(inline)函数。Clock类中的成员函数就是放在类内定义的。
C++可以在类内声明成员函数的原型,在类外定义函数体。这样做的好处是相当于在类内列了一个函数功能表,使我们对类的成员函数的功能一目了然,避免了在各个函数实现的大堆代码中查找函数的定义。在类中声明函数原型的方法与一般函数原型的声明一样,在类外定义函数体的格式如下:
返回值类型 类名 :: 成员函数名(形参表)
{
函数体;
}
::是类的作用域分辨符,用在此处,放在类名后成员函数前,表明后面的成员函数属于前面的那个类。
对象
类相当于一种包含函数的自定义数据类型,它不占内存,是一个抽象的“虚”体,使用已定义的类建立对象就像用数据类型定义变量一样。对象建立后,对象占据内存,变成了一个“实”体。类与对象的关系就像数据类型与变量的关系一样。其实,一个变量就是一个简单的不含成员函数的数据对象。建立对象的格式如下:类名 对象名;其中,对象名可以是简单的标识符,也可以是数组。在例5-1中,使用:Clock MyClock;建立了一个Clock型的对象MyClock。在建立对象后,就可以通过对象存取对象中的数据成员,调用成员函数。存取语法如下:对象名.属性对象名.成员函数名(实参1, 实参2,…,)例如,通过对象MyClock使用成员函数SetTime()的方式如下:MyClock.SetTime(8,30,30);至于对数据成员H、M、S的存取,因其存取权限为pravate而被保护, 所以不能进行直接存取。
注意:
为节省内存,编译器在创建对象时,只为各对象分配用于保存各对象数据成员初始化的值,并不为各对象的成员函数分配单独的内存空间,而是共享类的成员函数定义,即类中成员函数的定义为该类的所有对象所共享,这是C++编译器创建对象的一种方法,在实际应用中,我们仍要将对象理解为由数据成员和函数成员两部分组成。
构造函数
1. 不带参数的构造函数
构造函数可以不带参数,新建对象的各数据成员被赋予固定值。
2. 带参数的构造函数
带参数的构造函数首部:构造函数名(类型名1 形参1,类型名2 形参2,……,类型名n 形参n)
对于带参数的构造函数,定义对象时的格式如下:
类名 对象名(实参1,实参2,……,实参n);
3. 带默认值的构造函数
在定义对象时,如果给出了对应的实参值,则形参取实参传递过来的值,如果没有给出对应的实参值,则形参取默认值。
4. 带初始化表的构造函数
带参数的构造函数Rectangle()改写成带初始化表的构造函数如下:Rectangle::Rectangle(int len,int w):length(len),width(w){ }
某些性质的数据成员(如const数据成员),只能通过初始化表进行初始化。
5. 构造函数的重载
和普通函数一样,构造函数也可以重载。
析构函数
析构函数具有以下特点:
1. 析构函数没有任何参数,不能被重载,但可以是虚函数,一个类只有一个析构函数。
2. 析构函数没有返回值。
3. 析构函数名与类名相同,但在类名前加上一个逻辑非运算符“~”,以
4. 示与构造函数对比区别。
5. 析构函数一般由用户自己定义,在对象消失时由系统自动调用,如果用户没有定义析构函数,系统将自动生成一个不做任何事的默认析构函数。
复制构造函数
拷贝构造函数是一种特殊的构造函数,C++提供的拷贝构造函数用于在建立新对象时将已存在对象的数据成员值拷贝给新对象,即用一个已存在的对象去初始化一个新建立的对象。拷贝构造函数是与类名相同,其形参是本类的对象的引用。
类的拷贝构造函数一般由用户定义,如果用户没有定义构造函数,系统就会自动生成一个默认函数来进行对象之间的位拷贝(bitwise copy),这个默认拷贝构造函数的功能是把初始值对象的每个数据成员的值依次复制到新建立的对象中。因此,也可以说是完成了同类对象的克隆(clone),这样得到的对象和原对象具有完全相同的数据成员,即完全相同的属性。事实上,拷贝构造函数是由普通构造函数和赋值操作符共同实现的。用户可以也可以根据实际问题的需要定义特定的拷贝构造函数来改变缺省拷贝构造函数的行为,以实现同类对象之间数据成员的传递。如果用户自定义了拷贝构造函数,则在用一个类的对象初始化该类的另外一个对象时, 自动调用自定义的拷贝构造函数。
定义一个拷贝构造函数的一般形式为:
类名(类名& 对象名)
{
…
};
拷贝构造函数在用类的一个对象去初始化该类的另一个对象时调用,以下三种情况相当于用一个已存在的对象去初始化新建立的对象, 此时, 调用拷贝构造函数:
① 当用类的一个对象去初始化该类的另一个对象时。
② 如果函数的形参是类的对象,调用函数时,将对象作为函数实参传递给函数的形参时。 ③ 如果函数的返回值是类的对象,函数执行完成,将返回值返回时。
类的继承、派生
派生类定义的语法为:
class 派生类名:继承方式1 基类名1, 继承方式2基类名2,…
{
private:
派生类的私有数据和函数
public:
派生类的公有数据和函数
protected:
派生类的保护数据和函数
};
“继承方式1 基类名1, 继承方式2 基类名2,…”为基类名表, 表示当前定义的派生类的各个基类。如果基类名表中只有一个基类,表示定义的是单继承;如果基类名表中有多个基类,表示定义的是多继承。继承方式指定了派生类成员以及类外对象对于从基类继承来的成员的访问权限
继承方式有三种:public: 公有继承;private: 私有继承;protected:保护继承。
虚基类
为了解决从不同途径继承来的同名的数据成员在内存中有不同的拷贝造成数据不一致问题,将共同基类设置为虚基类。这时从不同的路径继承过来的同名数据成员在内存中就只有一个拷贝,同一个函数名也只有一个映射。这样不仅就解决了二义性问题,也节省了内存,避免了数据不一致的问题。
虚基类的定义是在融合在派生类的定义过程中的,其定义格式如下:
class 派生类名:virtual 继承方式 基类名
其中:
virtual是关键字,声明该基类为派生类的虚基类。
在多继承情况下,虚基类关键字的作用范围和继承方式关键字相同,只对紧跟其后的基类起作用。
声明了虚基类之后,虚基类在进一步派生过程中始终和派生类一起,维护同一个基类子对象的拷贝。
虚函数
纯虚函数(pure virtual function)是一个在基类中说明的虚函数,它在该基类中没有定义具体实现,要求各派生类根据实际需要定义函数实现。纯虚函数的作用是为派生类提供一个一致的接口。纯虚函数定义的形式为:virtual 函数类型 函数名(参数表)=0;
实际上,它与一般虚函数成员的原型在书写格式上的不同就在于后面加了=0。C++中,有一种函数体为空的空虚函数,它与纯虚函数的区别为:
① 纯虚函数根本就没有函数体,而空虚函数的函数体为空。
② 纯虚函数所在的类是抽象类,不能直接进行实例化,空虚函数所在的类是可以实例化的。
③ 它们共同的特点是都可以派生出新的类,然后在新类中给出新的虚函数的实现,而且这种新的实现可以具有多态特征。
友元
1. 友元函数是在类定义中由关键字friend修饰的非成员函数。其格式为 :
friend 返回类型 函数名(形参表)
{
… //函数体
}
友元除可以是函数外,还可以是类,如果一个类声明为另一个类的友元,则该类称为另一个类的友元类。若A类为B类的友元类,则A类的所有成员函数都是B类的友元函数, 都可以访问B类的任何数据成员。友元类的声明是在类名之前加上关键字friend来实现。声明A类为B 类的友员类的格式如下:
class B {
…
friend class A;
}
运算符重载
运算符重载就是在不改变运算符现有功能的基础上,为运算符增加与现有功能类似的新功能,即扩充运算符的功能
C++提供了运算符重载机制,通过对现有运算符重载,可以实现自定义类型数据的直接运算。重载运算符的函数定义格式如下:
类型名 operator运算符(形式参数表)
{对运算符的重载处理}
类模板
类模板是能根据不同参数建立不同类型成员的类。类模板中的数据成员、成员函数的参数、成员函数的返回值可以取不同类型,在实例化成对象时,根据传入的参数类型,实例化成具体类型的对象。类模板也称模板类。模板是C++语言的重要特征,它能够显著提高编程效率。 C++语言引入模板技术,它使用参数化的类型创建相应的函数和类,分别称之为函数模板和类模板。
函数模板
函数模板是参数化的通用函数。 C++中的模板并不是一个实实在在的的函数或类,它们仅仅是逻辑功能相同而类型不同的函数和类的一种抽象,是参数化的函数和类。利用C++的函数模板和类模板,能够快速建立具有类型安全的类库集合和函数集合,进行大规模软件开发,并提高软件的通用性和灵活性。
可以用来创建一个通用功能的函数,以支持多种不同形参,进一步简化重载函数的函数体设计。函数模板定义后,就可以用它生成各种具体的函数(称为模板函数)。在函数调用时,用函数模板生成模板函数实际上就是将模板参数表中的参数化类型根据实参实例化(具体化)成具体类型。这个过程称为模板的实例化。
函数模板实例化分为显式实例化与隐式实例化。
显式实例化:函数名<具体类型名1,具体类型名2,…, 常量表达式> (实参表)
隐式实例化:隐式实例化的格式为函数调用式,实例化过程是在实参与形参结合时,用实参的类型实例化形参对应的参数化类型。
例如: 使用add( ‘A’,‘B’) 将T add(T x, T y) 实例化成:char add(char, char)
异常处理
1. 抛出异常
您可以使用 throw 语句在代码块中的任何地方抛出异常。throw 语句的操作数可以是任意的表达式,表达式的结果的类型决定了抛出的异常的类型。
double division(int a, int b) { if( b == 0 ) { throw “Division by zero condition!”; } return (a/b); }
2. 捕获异常
catch 块跟在 try 块后面,用于捕获异常。您可以指定想要捕捉的异常类型,这是由 catch 关键字后的括号内的异常声明决定的。
try
{
// 保护代码
}catch( ExceptionName e )
{
// 处理 ExceptionName 异常的代码
}