1. 覆盖一个基类方法将导致基类所有同名函数被隐藏而无法从子类直接访问,所谓覆盖基类方法是指子类函数名、参数列表、const关键字与父类一样(返回类型可以不同)。(覆盖隐藏)
2. 虚继承解决了多重继承代码产生多义性的问题,将共同的基类合并,消除多义性。通常,类的构造函数只初始化自己的变量及其基类,但虚继承是基类例外,他们由最后的派生类进行初始化,因此使用虚基类时务必在最后的派生类中初始化共同基类。公有基类不接受虚继承的派生类对其的初始化参数,虚继承使得多个派生共享一个公有基类(虚继承)
3. 虚函数的强大作用,只有使用指针或引用才能发挥,按值传递不行。(虚函数)
4. 纯虚函数必须要在子类中覆盖具体实现,含有纯虚函数的类不能实例化,只能派生; (纯虚函数、抽象类)
5. 在const成员函数中调用非const成员函数 以及 const对象调用非const成员函数 都是错误的。const成员函数可以用非const对象重载,编译器会根据对象是否为const自动调用相应的重载版本,且const对象只能调用const方法,而非const对象却可以调用const方法。在const对象的构造函数中调用非const成员函数是合法的。构造函数不能为const(const)
6. 用关键字static声明的局部变量仍然只在定义该变量的函数中使用,但与自动变量不同的是,static局部变量在函数退出时保持其数值。下次调用这个函数时,static局部变量包含上次函数退出时的值(static)。
7. 下面是不需要包含头文件iomanip就可以使用的控制符:Flush、Endl、Oct、dec、hex
下面是要需要包含头文件iomanip才可以使用的控制符:
Setbase(base):设置计数方式(0、8、10、16)
Setw(width)、setfill(ch)、setprecision(p)、setiosflags(f)、resetiosflags(f)
除flush、endl、setw外,其他控制符在被修改以前一直保持有效。
使用cin.getline前考虑是否需要调用cin.ignore,已删除缓冲区内的换行符。(输入输出流)
8. 指针占用4个字节,因此指针数组占用N*4个字节,比如char* pstr[20],共占用80个字节。对于char *p=new char[20],在自由区分配了20个char型连续内存,因为每个char占用1个字节,因此共占用20个字节,p是第一个char的地址。
9. 友元函数必须定义在类外,其定义的形式与非成员函数的形式相同,不需要加类限定。(模版)。
10. C++提供五种容器:向量、链表、堆栈、双端队列、队列
11. ostream和istream都是std成员,类似cout和cin,使用时需要注意限定或添加声明。
12. 函数接受数组参数的三种方式:fun(int a[])、fun(int a[30])、fun(int * a)。
13. 求非字符串数组长度:int size=sizeof(a)/sizeof(int),但如果sizeof的操作数是函数中的数组形参,将只能得到指针的大小。
14. 二分法查找元素,最多需要循环(int)log2(N)+1次,缺点:数组必须顺序排列;遇到重复元素,无法找全。(二分法)
15. char、short、int、long在不同的编译器上长度可能不一样,Microsoft VC++里面,char(1 byte),short(2 bytes),int(4 bytes),long(8bytes),有些编译器的这四个类型长度可能部分相同,如Borland C++上,int和short都是2bytes。
16. C99定义了两种新类型,即long long和unsigned long long,这两种至少64位。
17. wchar_t(宽字符类型,可能为unsigned short),wchar_t bob=L’p’;
18. float至少32为,double至少48位,long double至少和double一样多,通常float(32bits),double(64bits),long double(80\96\128bits),C++只保证float 6位有效位,若要更高精度,使用double,例如:float f=41.419998,hat=52.210000;cout<<f+hat<<endl;结果为93.63;因为41.419998四舍五入为6位41.4200;带小数的常数如1.2将默认为double型,加上后缀f才标识为float型。
19. 声明数组char a[10]后,a中内容不定,如用strcat,需要事先数组清零。也可以在数组声明时初始化为0,只需将第一个元素置0,编译器会将其他元素都置0;
20. cin读取输入字符串时,只读取第一个空格前的字符串,空格后的字符串留在缓冲区等待下次读取。为了解决这个问题,使用cin.getline(char* buf,int len );它将换行符也从缓冲区取出,但把换行符用空格代替。cin.get()与cin.getline()类似功能,但不读取换行符,把他留在缓冲区内,因此不能两次连续使用cin.get(char*,int);但可以使用cin.get()将这个换行符从缓冲区取出解决问题。总之getline更方便,但get更细致,可以判断输入结束是因为数组满了还是输入超长。cin本身读取字符串时,也将换行符留在缓冲区,此后如果再调用getline()将把这个换行符看成空行,得不到任何想要的输入。cin读取数据时将不理会输入字符串前面的所有空格和换行。如输入:” 1234”,将得到”1234”; cin.getc(char)用于读取下一个字符,与ch=cin.get()一样。
21. cout.write(char_type * str,int n)//显示n个字符
22. 使用strcpy或strcat时,如果目标数组不够长,将导致相邻内存被复写,导致数据损坏或程序崩溃。String类提供了strncpy和strncat,接受目标数组最大允许长度,以保证安全。
23. 结构体struct与共用体union:struct可以同时存储多个数据,其长度为各数据类型总长,union定义与struct一样,但每次只能存一种数据,其长度为最大数据类型的长度。struct structA{int a;double b;union{int id_Int;char id_Str[20];};};上面的unoin是个匿名共用体,其两个成员将自动属于structA。
24. 枚举中的值可以相同,如enum{zero,zero2=0,one,one2=1}=>{0,0,1,1};枚举值必须是定义过的,但后来C++通过强制类型转换,增加了可赋给枚举变量的合法值,每个枚举都有取值范围,可以将取值范围内的任何整数赋给枚举变量,即使这个值不是枚举值。如定义enum bits{one=1,two=2,four=4,eight=8};则代码bits myfalg=bits(6);是合法的,即使6不是枚举值。枚举的上限为大于最大值的属于2次幂的最小值减1,如上例bits中,上限为16-1=15;下限根据最小值是否小于0判断,不小于0,则下限为0,小于0则下限与上限计算一样,只不过最后需要加一个负号。另外不需要指定枚举使用何种数据类型存储枚举值,编译器会自动选择,对于较小的数据,可能会使用char。
25. 给指针赋地址:错误:int *pt=0x00380000,正确:int *pt=(int*)0x00380000;
26. 对于已经释放的内存使用delete是危险的,但对指针使用delete是安全的。
27. 延时:传统上使用while(wait<10000) wait++;来延时,但不同的机器执行完这段代码的时间也不同。C++库有个函数有助于完成这样的工作:clock(),返回程序开始执行以后的系统时间。但clock()返回的不是秒,而是系统单位时间数,一秒的系统时间数由常量CLOCS_PER_SEC(ctime中),Microsoft C++定义其=1000;即clock()返回毫秒时间,clock()/CLOCS_PER_SEC即秒;因此这样延时
float sec =5.5f;clock_t delay=sec*CLOCKS_PER_SEC;clock_t start=clock(); while(clock()-start<delay);
28. 文件尾条件:输入时如想像文本编辑一样接纳空格、回车,只在一切都输完了以后才结束。可以使用EOF标志来模拟文件尾条件。在DOS上是Ctrl+Z、Enter;在UNIX上是Ctrl+D;当系统检测到EOF后,cin将两位eofbit和failbit都设置为1,通过bool cin.eof()来查看该eofbit位是否置1,同样bool cin.fail()返回failbit状态。如例:char ch;while(cin.fail()==false){cin.get(ch);cout<<ch;}将不停接受输入,直到在新行按下EOF模拟键;当检测到EOF后,后面的cin调用都将失效。这将导致后面无法继续输入,可以使用cin.clear()来清除EOF标记,是输入可以继续进行。
29. 字符类型判断:#include<CCTYPE>
字母isalpha 数字或字符数字isalnum数字isdigit 空白isspace 标点符号ispunct 空格或制表符isblank大写isupper 小写islower转大写toupper 转tolower
30. 文件输入输出:#include < FSTREAM >,ofstream继承自ostream:
ofstream fileout;fileout.open("123.txt");fileout<<"hellofilxe"<<endl;ofstream与cout用法相同。 ifstream filein;filein.open(“123.txt”);if(filein.is_open( )) filein>>str;filein.close() ifstream与cin用法相同;while(filein.good( )){…} good用于判断文件打开良好且未到EOF, in.eof()可判断是否到文件尾
31. 文件打开模式File.open(“file”,mode):
ios_base::in读取
ios_base::out:写入
ios_base::ate:移到文件尾
ios_base::app:追加
ios_base::trun:截断文件
ios_base::binary:二进制打开
各模式可以或运算相加,如ios_base::in|ios_base::binary
32. 文件操作
C C++
int fseek(FILE *fp, LONG offset, int origin) ostream& seekp( streampos pos );
ostream& seekp( streamoff off, ios::seek_dir dir );
istream& seekg( streampos pos );
istream& seekg( streamoff off, ios::seek_dir dir );
ftell(FILE*fp) tellg()
33. 使用临时文件<cstdio>: char *tmpnam(char* pszName)创建一个临时文件名,存到pszName字符串中,文件名长度通常由常量L_tmpnam指定。
34. const数据是禁止取其地址作为指针的,因为如果这样做就可以通过指针修改该变量了。如const float a=1.6; float * pf=&a(错误);
35. <sstream>头文件定义了一个从ostream类派生的ostringstream类(当然还有一个wostringstream),可以将cout的方法用于该类对象,即
ostingstream outstr;outstr<<”Pay only $”<<price<<ccharstr<<endl;
string mesg=outstr.str();//返回字符串对象
36. 二维数组的指针形式a[3][4]=*(*(a+3)+4),需要说明*(a+3)是第4个元素的数组名,即数组首地址;int *r[10]是声明为一个指向int的指针组,长度为10,占用空间4*10bytes,比如 char *pts[10]。(*p)[4]声明为一个列数为4,行数不定的二维数组p,也就是指向4个int的组成的数组的指针的指针,p占用空间4bytes,分配内存:p=(int(*)[4]) new int(4*k), 即二维数组a的原型为(*p)[4],就可以这样在函数中使用它p[1][2]:=a[1][2];int **t声明为指向int的一群指针的指针,t=(int **)new int *[10]分配了10个一级指针空间,然后t[0..9]=new int[20]每个指针赋为指向20个int的首地址。
37. 函数指针:如函数void show( ){cout<<show<<endl;}将显示该函数的地址。函数指针的声明:如double func(int);其指针声明为:double (*myfun)(int),这样如double (*myfun)(int)=func;myfun就可以代替func使用了。
38. 声明引用时必须初始化。按引用传参是C++才有的,C中没有。
39. 函数模版定义:类似于template<typename T>
T& func(T &t1,int a){…};
模版的具体化:template<> int& func<int>(int &t1,int a);
普通函数:int& func(int &t1,int a);
当调用func时,匹配的优先级:普通函数>模版具体化函数>模版
模版的显式实例化:template int& func<int>(int &t1,int a);
40. c++支持register关键字,来声明CPU寄存器型局部变量,提高访问速度。寄存器变量没有地址可言,因此不能用于指针,或取址。
41. C++存储数据的三种不同方案:方案的区别就在于数据保留在内存中的时间:
1.自动存储持续性——在函数定义中声明的变量(包括函数的形参)的存储持续性为自动的。
2.静态存储持续性——在函数定义外定义的变量和使用关键字static定义的变量的存储持续性都为静态。他们在程序的整个运行过程中都存在。C++有三种存储静态的变量。编译器分配固定的内存块来存储所有的静态变量。在整个程序的运行期间一直存在。
创建3种静态的持续变量:1.外部链接的静态持续变量,必须在代码块的外面声明它;2.内部链接的静态持续变量,必须在代码块的外面声明它,并且用static限定符;3.无链接的静态持续变量,必须在代码块中声明,并使用static限定符。
3.动态存储持续性——用new操作符分配的内存将一直存在,知道使用delete操作符将其释放或程序结束为止。
举例: int global=100;//外链接静态变量,可以在其他文件中使用
static int one_file=30;//内链接静态变量,只能在本文件中使用
int main()
{......}
void fun1(int n){
static int count =0;//无链接静态变量,即使未执行也始终占用内存
}
42. 如果想要一个const变量成为外部变量,需要在前面加上extern关键字,这是与常规外部变量所不同的,在其他文件中使用该变量时也需要使用加上extern。如:1.cpp: extern const int extInt=12; 2.cpp: extern const extInt;//使用extInt。
43. 静态存储变量的初始化必须是常量表达式,如int *ptr=new int[20]就是错误的,因为new int[20]是在运行时才知道返回的结果(首地址)。
44. static函数只能在本文件中可见,外部文件无法使用。这意味着可以在不同的文件中定义相同名称的static函数。与外部变量一样,使用外部函数前也要声明(外部函数不能为static)。
45. extern “C”(注意’C’大写)用于处理C++与C的相互调用:首先注意C编译器处理函数名称的方法是在函数名前加’_’,如函数原型void func(int,double);编译后时函数名修饰为_func,而C++编译器则会将其修饰为_func_int_double,C++就是这样解决函数重载问题的,因此C++和C的相互调用可能因为名称无法匹配而找不到目标函数。extern “C”就是用来解决这个问题的,extern “C”只能用在C++代码中:
1.C++调用C:如:1.c定义了int rtn(int); 1.cpp要调用1.c中的函数,则在1.cpp中声明extern “C”{extern int rtn(int);}来告知C++编译器这个函数使用C编写的,应该用C编译器对函数名修饰的规则来寻找此函数(在链接阶段才寻找);或者如果C代码有同名头文件,则在C++中使用extern “C”{#include “1.h”}
2.C调用C++:在1.cpp中定义extern “C”{ void show(int x); }来告知C++编译器使用修饰C函数的规则修饰这个函数,以便其他C编译器可以找到该函数。在1.c中先声明extern show(int);再使用该函数,而不应该使用#include “1.h”来包含C++的头文件,这件导致连接错误。
通常在C++头文件中有这样一段:
#ifdef __cplusplus
extern "C" {
#endif
//一段代码
#ifdef __cplusplus
}
#endif
这告诉编译器如果这是一段C++代码,则外加上extern “C” { }来以C的方式处理这段代码,以便其他C可以访问这段代码,其中__cplusplus是C++的默认宏(C的默认宏是__stdc__),这两个宏定义是在编译阶段定义的,我们看不到。
46. new 的变体布局new操作符,可以在指定内存空间分配内存:
char buf[100]; char *ptr=new (buf)char[50];便在buf中又分配了50个char的空间;那么ptr指向的头一个char就是buf[0];另外不可以使用delete 释放ptr;通常显式调用析构函数来释放内存。
47. ::varible 指本命名空间或本文件中的gloable变量。
48. C++不赞成用static限制一个全局变量的使用范围,而推荐使用namespace:如:namespace{int gloable=1;} void func( ){..}; …void func2( ){..}….
49. cerr和cout的区别:cout的输出可以重定向到一个文件中(如在命令行使用test.exe>log.txt实现输出重定向),而cerr必须输出在显示器上,无法重定向。cout是标准输出流,与cerr的区别在于cerr不经过缓冲区,直接向显示器输出信息,而cout中的信息存放在缓冲区,缓冲区满或者遇到endl时才输出.
50. 隐式调用类的默认构造函数:Person per或 Person *per=new Person;(不带括号),三种方法定义默认构造函数:1.带默认值有参构造函数2.无参构造函数.3.不定义任何构造函数,编译器才会提供一个默认构造函数。 一旦程序员定义了一个非上面三种构造函数,要使用默认构造函数,程序员必须自己再定义,编译器不会再提供。一般使用对象数组时可能必须要求对象有默认构造函数。
51. 注意到:Person sp2=Person(12);与Person sp2; sp2=Person(12);有着根本区别:第一个是初始化,一般不会创建临时Person对象,而第二个一定会创建一个临时对象,然后将该对象复制给sp2对象;
52. 在类中要想设置常量,做法class cls{private: const int LEN=10;}是行不通的,因为类只是描述形式,并不会创建对象的存储空间。通常做法是2种:
1.定义枚举,如class cls{private :enum{LEN=10};}这里枚举可以没有名称,这并会创建LEN的存储空间,只是编译器在遇到LEN时,将其替换为10;
2.使用static,如class cls{private:static const int LEN=10; }这里LEN不会在对象中存储,而是与其他静态变量存在一起,各对象共享该数据。但第二种方法Microsoft C++并不支持。
53. const对象的所调用的方法体需要添加const,以保证该方法不会修改该对象:
如class Person{public: void show()const{};} const Person per=new Person; per.show( );
54. 为什么使用友元函数:为了解决类似Time newtime=2 + timeold;而无法使用类的成员重载运算符函数,对于二元运算符重载经常需要友元函数。
55. 只含一个参数的构造函数的类,可以包含隐式转换,例如Integer Int=12;若禁止这种看不见的转换(这通常是需要的),可以通过explicit关键字,如构造函数explicit Integer(int x=0);但这并不妨碍显示转换,如Integer Int=(Integer)12;
56. 隐式逆转换:即对象转换成基本型:operator int( )、operator double( );等等explicit不能用于该转换函数。
57. main函数不一定就是第一个被执行的函数,可以定义一个全局类对象,该对象可以在main执行前被初始化。
58. C++隐式成员函数:自动创建下面5种函数:
1.默认构造函数
2.复制构造函数
3.赋值操作符
4.默认析构函数
5.地址操作符
59. 编译器对类成员函数的编译存在两种类型,非虚方法将采用早期联编(静态联编);而虚方法将采用晚期联编(动态联编);虚函数的管理方法是采用虚函数地址表的形式。
60. 对于子类的构造函数、复制构造函数、赋值函数等若覆写这些方法,需要显式调用其基类的构造函数、复制构造函数、赋值函数等完成基类的构造和复制。
61. 子类的友元函数通过类型强制转换调用基类的友元函数,例如os<<(animal)ahen;os<<ahen;(其中ahen是继承于animal的子类对象)
62. 函数返回临时对象,不能使用其引用。
63. 派生类私有继承于基类,在派生类中可以直接通过类名::方法名来调用基类方法,而公有继承只能使用对象来访问其基类方法。
64. 在使用私有、保护继承时,若要在类外使用基类方法,一是:定义使用基类方法的派生类,通过访问派生类来访问基类。而是使用using关键字来改变特定的方法的权限,将要使用基类方法列举在Public下,效果就好像这些方法是公有继承来的,这样派生类对象就可以直接访问基类方法了。
65. 类模版的具体化:
1.显示实例化(就是指定所有泛型);格式:template class myclass<string,int>;
2.隐式实例化(创建模版对象时指定泛型),格式 myclass<string,int>;
3.模版具体化(就是在需要定制模版时,重写整个模版类);格式 template<> class myclass<string,int>{…..}; 注<>表示没有未知的泛型,即全部具体化了。
4.部分具体化(未全部指定泛型的类型);格式 template<class T> class myclass<T,int>; 注<>表示没有被具体化的泛型参数。
66. 成员模版:分为成员类模版和成员函数模版(即可以在模版类中定义另一个模板类,并定义一个实例作为该类的成员);三类模版的友元:非模版友元—>约束模版友元—>非约束模版友元;
67. 名称 是否是模版 泛型约束
非模版友元 否 是
约束模版友元 是 是
不约束的模版友元 是 否
79. 非模版友元是特殊的不约束的模版友元,它的泛型和类模版一样,因此可以随类具体化,从而成为非模版友元,但不约束的模版友元的泛型和类模版的不一样,无法随类具体化,成员一种模版,这是它俩的区别。
具体见stack文件夹
80. 类的编译优先问题:(一个类的函数作为另一个类的友元函数)如
“tv.h”:
include ”controler.h”
class tv{
friend void controler::remotetoggle(tv &t);//定义类的友元
}
“controler.h”:
include ”tv.h”
class controler
{
void remotetoggle(tv &t);{t.on=!t.on;};
}
这两个类的头部相互包含,没法编译成功。一种做法是使用前向声明,如下:
“controler.h”:
class tv;//前向声明:tv是个类,这个类在后面定义
class controler
{
void remotetoggle(tv &t);{t.on=!t.on;};
}
“tv.h”:
include ”controler.h”
class tv{
friend void controler::remotetoggle(tv &t);//定义类的友元,指出了controler类的内部成员函数,因此不能使用前向声明解决优先编译问题。
}
但是一个问题是:{t.on=!t.on;};指出要访问tv类的on属性,但前向声明无法指出tv类中有没有这个东西,编译无法经行;做法是将方法体移到最后再定义,这必须将两个类放到同一个文件:如下:
“tv_ctl.h”:
class tv;//前向声明:tv是个类,这个类在后面定义
class controler
{
void remotetoggle(tv &t);
}
class tv{
friend void controler::remotetoggle(tv &t);//定义类的友元
}
inline void control::remotetoggle(tv &t){ t.on=!t.on;};
81. 将整个类作为另一个类的友元类,不存在上面的编译优先问题:
“tv.h”:
class tv{
friend class controler;//不需要”control.h”,否则出现循环包含问题,相当于前向声明的作用,指出了controler将是个类,这个类在后面定义。
}
“controler.h”:
include ”tv.h”
class controler
{
void remotetoggle(tv &t);{t.on=!t.on;};
}
82. 处理异常:<cstdlib> 用std::abort()结束程序,也可以用cerr输出信息。使用throw和try…catch:trow什么类型的数据,就catch什么类型的数据.如:
if(a = =-b) thorw “illeage int a and b”;--->catch(const char *e){cerr<<e<<endl;}
可以在函数声明处指出该函数将抛出哪种异常,如int divide(int x,int y) throw(int);
catch的异常数据如string e,它总是发生异常时编译器产生的临时拷贝(这要求抛出的对象必须实现了正确的拷贝功能),因为异常函数已经结束,原来的数据已经不复存在,但一般仍使用引用&,这避免了一此拷贝。使用省略号俩表示捕获所有异常或未知异常,如catch(…)
83. ① 如果catch中使用基类对象接收子类对象,那么会造成子类对象分隔(slice)为父类子对象(通过调用父类的复制构造函数);
② 如果catch中使用基类对象的引用接受子类对象,那么对虚成员的访问时,会发生动态绑定,即会多态调用。
③ 如果catch中使用基类对象的指针,那么一定要保证throw语句也要抛出指针类型,并且该指针所指向的对象,在catch语句执行是还存在(通常是动态分配的对象指针)。
84. 栈展开:栈展开指的是:当异常抛出后,匹配catch的过程。
抛出异常时,将暂停当前函数的执行,开始查找匹配的catch子句。沿着函数的嵌套调用链向上查找,直到找到一个匹配的catch子句,或者找不到匹配的catch子句。注意事项:
1. 在栈展开期间,会销毁局部对象。
① 如果局部对象是类对象,那么通过调用它的析构函数销毁。
② 但是对于通过动态分配得到的对象,编译器不会自动删除,所以我们必须手动显式删除。(这个问题是如此的常见和重要,以至于会用到一种叫做RAII的方法)
2. 析构函数应该从不抛出异常。如果析构函数中需要执行可能会抛出异常的代码,那么就应该在析构函数内部将这个异常进行处理,而不是将异常抛出去。
3. 构造函数中可以抛出异常。但是要注意到:如果构造函数因为异常而退出,那么该类的析构函数就得不到执行。所以要手动销毁在异常抛出前已经构造的部分。
85. 异常重新抛出
语法:使用一个空的throw语句。即写成:throw;
注意问题:
① 重新抛出异常的位置,只能是catch子句中或者是catch子句调用的函数中。
② 重新抛出的是原来的异常对象,即上面提到的“临时变量”,不是catch形参。 ③ 如果希望在重新抛出之前修改异常对象,那么应该在catch中使用引用参数。如果使用对象接收的话,那么修改异常对象以后,不能通过“重新抛出”来传播修改的异常对象,因为重新抛出不是catch形参,应该使用的是 throw e; 这里‘e’为catch语句中接收的对象参数
86. 未捕获异常:如果程序中有抛出异常的地方,那么就一定要对其进行捕获处理。否则,如果程序执行过程中抛出了一个异常,而又没有找到相应的catch语句,那么会和“栈展开过程中析构函数抛出异常”一样,会调用terminate函数,而默认的terminate函数将调用abort函数,强制从整个程序非正常退出。
87. 构造函数的函数测试块,对于在构造函数的初始化列表中抛出的异常,必须使用函数测试块(function try block)来进行捕捉。语法类型下面的形式:MyClass::MyClass(int i) try :member(i) {
//函数体
} catch(异常参数) {
//异常处理代码,内存释放,因为析构函数不会执行
}
注意事项:在函数测试块中捕获的异常,在catch语句中可以执行一个内存释放操作,然后异常仍然会再次抛出到用户代码中。
88. 如果函数没有显式的声明抛出列表,表示异常可以抛出任意列表;throw()表示不抛出任何异常
89. 如果你声明了抛出列表,即使你的函数代码中抛出了没有在抛出列表中指定的异常,你的程序依然可以通过编译,到运行时才会出错,对于这样的异常,在C++中称为“意外异常”。如果程序中出现了意外异常,那么程序就会调用函数unexpected()。这个函数的默认实现是调用terminate函数,即默认最终会终止程序,可以通过set_unexpected(*handler)来指定发生意外异常的处理手段。
90. 虚函数重载方法时异常抛出列表的限制:在子类中重载时,函数的异常说明 必须要比父类中要同样严格,或者更严格。换句话说,在子类中相应函数的异常说明不能增加新的异常。或者再换句话说:父类中异常抛出列表是该虚函数的子类重载版本可以抛出异常列表的超集
91. C++标准库异常:(见附录A)
logic_error类及其子类、runtime_error类及其子类,它们的构造函数是接受一个string类型的形式参数,用于异常信息的描述,所有的异常类都有一个what()方法,返回const char* 类型(C风格字符串)的值。
92. new关键字引发的异常,头文件<new>中定义了无法分配内存的异常bad_alloc类异常,该类派生于exception类,通过set_new_handler(*handler)可以指定内存分内失败后的处理方法,而不会直接抛出异常,或者使用无异常版本的new,若失败则返回NULL,不会有异常。
93. 程序未捕获到存在的异常是,将自动调用terminate()(在头文件<exception>定义),该函数默认会调用abort()结束程序,可以通过设置terminate的处理方法来实现对未捕获的异常的处理,如:在程序开始时set_terminate(log);其中log函数定义为void log(){//用于处Uncaught Exceptions
cerr<<"uncaught exception"<<endl;
exit(5);//一定要退出
}
94. RTTI:运行阶段类型识别,C++支持RTTI的三个元素:
1.dynamic_cast用于将基类类转换为派生类,可以是指针或引用,对于指针如无 法转换则返回空指针,对于引用则抛出bad_cast异常,如Person &p=dynamic_cast<Person &> stu。
2.typeid返回一个指出对象类型的值,用于确定两个对象是否同种类型,如typeid (Person)= =typeid(*stu),此时若stu为空指针则引发bad_typeid异常
3.type_info存储了有关特定类型的信息。typeid本身返回一个type_info对象的引 用,type_info类包含一个name方法,用于指出对象类型的名称,如 typeid(*stu).name()一般是“class student”
注意:RTTI只应该适用于包含虚函数的类。
程序片段:(getName( )是个虚方法)
Person *Per=new student(12,"qylk");
student *stu=dynamic_cast<student *>(Per);
cout<<typeid(*Per).name( )<<endl;
if(stu)
cout<<stu->getName( )<<endl;
if(typeid(student)==typeid(*Per))
cout<<"Per can be a student"<<endl;
delete stu;
95. dynamic_cast限制了转换的方向,即向上转换,而不允许其他转换,使得转换过程变得严格和可预见。和dynamic_cast类似的还有三个static_cast、const_cast和reinterpret_cast,const_cast最好不用。static_cast要求被转换的和转换的类型存在关系,不管是上下还是下上,还是平级关系,这可以用来将整型转换为枚举值。如 enum NUM{ONE,TWO,THREE};
NUM TH=static_cast<NUM>(3);
cout<<TH<<endl;
96. <memory>:auto_ptr(智能指针)是一个指针的包装模版类,它的的作用是在该类对象过期时自动释放指针指向的内存,它的用法和指针一样。如auto_ptr<student> strp(new student(12,"kitty"));
cout<<strp->getName()<<endl;
需要注意的是auto_ptr析构时采用delete,而非delete[ ],因此不可以传入数组指针给auto_ptr,而且传入的指针必须是指向堆上的(即使用new产生的).需要注意的是本来不允许auto_ptr的拷贝,即不允许将一个对象的指针赋给两个auto_ptr,因为这会导致两次析构同一对象,但auto_ptr采用了所权概念来避免这一问题,这意味着只有最后一个拷贝有权访问对象,其他拷贝无权,或通过引用计数,只有当引用为1时才可以析构。
97. STL-string:
string实际上是由模版实现的,系统定义了:
typedef basic_string<char> string;
typedef base_string<wchar> wstring;
启示我们可以自己定义自己的string:
typedef base_string<mytype> mystring;
98. 迭代器是一种广义的指针模版,它使得算法独立于容器类型,实际可以是普通指针或者对象,它实现了类似指针的*和++运算符,从而实现对容器类的遍历,每个容器类都实现了begin( )和end( )方法,分别把容器的第一个元素和和超尾位置赋给iterator,迭代器使得容器的遍历的方法抽象化,每个容器类应该实现自己的迭代器。
STL定义了五种迭代器:
1.输入迭代器,应该满足不修改容器的值,单向++,不能倒退。
2.输出迭代器,应该只接受数据来修改容器,不读取,类似于cout。
3.正向迭代器,只使用++遍历。
4.双向迭代器,比正向迭代器,拥有--x和x--运算
5.随机访问迭代器,要求能够直接跳到某位置
99. STL容器:1.deque:双端队列
2.list:双向列表
3.queue
4.priority_queue:大元素在前面
5.stack
6.vector 类似数组
100. 序列(6种标准容器)都应该支持的方法:见附录C
101. 联合容器:1.set 自动排序,不存在重值
2.multiset
3.map 可以通过[keyword]取值
4.multimap
102. STL通用算法函数:
1、非可变序列算法:指不直接修改其所操作的容器内容的算法。
2、可变序列算法:指可以修改它们所操作的容器内容的算法。
3、排序算法:包括对序列进行排序和合并的算法、搜索算法以及有序序列 上的集合操作。
4、数值算法:对容器内容进行数值计算。
103. 其他类库:<Complex>提供float、long、long double,提供了标准的复数运算函数。<valarray>提供了模板类valarray,用于表示数值数组,支持数值数组操作,例如先行运算等。举例:val3=10.0*((val1+val2)/2.0+val1*cos(val2))(都是相应元素的运算),valarray不支持自动调整大小,没有支持插入,排序,搜索的方法。
105. 流对象:cin、wcin、cout、wcout、cerr、wcerr、clog、wclog
106. 显示指针:将其转换为(void *),如cout<<(void *)val;
109. <CSTDLIB>:
随机取值:srand(int seed)->rand( );
110. <CTIME>:
当前时间(秒):time(0);
111. <VALARRAY>:
valarray<T>模版
112. strchr(“abcd”,choice)返回”abcd”字符串中首次出现choice字符串的位置,找不到返回NULL;
113. <CFLOAT> DBL_MAX表示无穷大