C++primer中文版(第四版)(个人笔记)

第一章    快速入门

1、 C++的大部分基本要素:内置类型、库类型(istream\ostream)?、类类型、变量、表达式、语句和函数。

2、 每个C++程序必须有main函数,且main函数是(唯一)被操作系统显示调用的函数。

3、 main函数的返回值必须使int型。

4、 返回0,代表正常。

5、 输入输出流iostream 输入cin, 输出cout

6、 Endl 是特殊值,称为操作符,将它写入输出流时,具有输出换行的效果,并刷新与设备相关联的缓冲区

7、 前缀std::表明coutendl是定义在命名空间std中。使用命名空间可以避免同名冲突。因为标准库定义的名字是定义在命名空间中的。

8、 While(cin>>x){}可以用于读取数目不明的数据 ctrl+z 结束。正确读取返回真,否者为假。

9、 自定义头文件,用“ ” 把头文件名括起来。

10、        使用点操作符(.)调用成员函数。如:item1.same_isbn()

第一部分 基本语言

第二章    变量和基本类型

1、 算术类型的存储空间大小依机器而定。所能表示的最大(最小)值也因机器的不同而有所不同。

2、 Unsigned 默认是unsigned int

3、 整数、字符和布尔值的算术类型合称为整型。

4、 虽然char是整型,但char类型通常用来存储字符而不用于计算,否则可能很容易出问题。

5、  400L(l) 代表长整形,浮点型默认为double类型,L”字符(串): 宽字符(串)

6、 所有的字符串字面值都是由编译器自动在末尾添加一个空字符A’代表个字符,A代表A和空字符两个字符。

7、 在一行的末尾加一反斜线符号(/)可将此行和下一行当作同一行处理。

8、 变量必有类型,类型决定了该变量在内存的大小和布局、取值范围以及操作集。变量又名对象。对象使内存中具有类型的区域

9、 变量名一般用小写字母,两种格式student_loan studentLoan。命名习惯最重要的使保持一致

10、        初始化分为两种,复制初始化(int ival=1024);和直接初始化(int ival(1024))直接初始化语法更灵活且效率更高。

11、        定义如何进行初始化的成员函数称为构造函数。一个类可定义多个构造函数,只要参数不同。

12、        初始化时,对象的名字立即变成可见,所以可以用同一个定义中前面已定义变量的值初始化后面的变量。

13、        建议每个内置类型的对象都要初始化。虽然不必需,但是更安全。永远不要依赖未定义行为。

14、        变量的定义用于为变量分配存储空间仅一次声明用于向程序表明变量的类型和名字,可多次,可以使用关键字extern声明变量名而不定义它,如:externint i ;不定义不分配存储空间。只有当extern声明位于函数外部时,才可以含有初始化式,如extern double pi=3.1416(相当于定义)。

15、        通常把一个对象定义在它首次使用的地方是一个很好的方法。

16、        Const定义常量,如:const int bufSize =512;

17、        与其他变量不同,在全局作用域声明的const 变量是定义该对象的文件的局部变量,其他文件不能访问。通过指定const变量为extern,就可以在整个程序中访问const对象。Extern const int bufSize =fcn();

18、        const变量默认为extern。要使const变量能够在其他的文件中访问,必须显示地指定为extern

19、        引用仅是对象的另一个名字。通过在变量名前添加“&”符号定义,如:int &refVal=I;

20、        ???“const引用” :指向const对象的引用。Const引用是只读的。

21、        const引用只能绑定到与该引用同类型的对象。Const引用则可以绑定到不同但相关的类型的对象或绑定到右值,如:double dval=3.14;const int &i=dal;

22、        Typedef可以用来定义类型的同义词。

23、        枚举的定义 : 关键字enum 枚举类型名 {枚举成员【是个常量】,逗号分隔},如:enum open_modes{input,output,append}。默认,第一个枚举成员赋值为0,后面递加1枚举成员是常量,需要用常量表达式初始化。

24、        C++中,通过定义类来自定义数据类型。类定义了该类型的对象包含的数据和该类型的对象可以执行的操作。类定义不要忘了{}分号。

25、        定义类的数据成员和定义变量的最大区别:一般不能把类成员的初始化作为其定义的一部分,而是用构造函数初始化。

26、        Struct也可以定义类,默认为public;而class默认为private

27、        ????头文件一般包含类的定义、extern变量的声明和函数的声明。因为头文件包含在多个源文件中,所以不应该含有变量或函数的定义。

第三章    标准库类型

1、 using std::cin 声明。但是,在头文件中必须使用完全限定的标准库名字。

2、 string类型支持长度可变的字符串。字符串字面值与标准库string类型不是同一种类型。

3、 cin>>string。读取并忽略开头所有的空白字符(如空格,换行符,制表符),读取字符至再次出现空白字符,读取终止。

4、 getline读取整行文本。Getlinecin,string)。不忽略开头空白符,以换行符结束。

5、 Str1 == str2 比较内容是否相同。

6、 为了避免溢出,保存一个string对象size()的最安全的方法就是使用标准库类型string::size_type,它是无符号整数。

7、 C++程序中,最好使用cname这种头文件的版本,而不采用name.h版本,这样标准库中的名字在命名空间std中保持一致

8、 Vector同一种类型的对象的集合,每个对象都有一个对应的整数索引值。

9、 Vector不是一种数据类型,而只是一个类模板,可以用来定义任意多种数据类型。Vector类型的每一种都指定了其保存元素的类型,如 vectorivec;

10、        虽然可以对给定元素个数的vector对象预先分配内存,但更有效的方法是先初始化一个空vector对象,然后再动态地增加元素。

11、        Vector对象的的size返回相应vector类型定义的vector<*>::size_type的值。使用push_back操作添加元素。

12、        C++程序员优先选用!=而不是<来编写循环判断条件。Vector可以动态增长,所以关于size大小的判断,写在循环内部。Vector的下标只能用于获取已存在的元素,不能添加任何元素

13、        迭代器是一种检查容器内元素并遍历元素的数据类型。迭代器类型提供了比下标更通用的方法:所有的容器都定义了相应的迭代器类型,而只有少数的容器支持下标操作。

14、        容器的iterator类型 vector::iterator iterBegin:第一个元素,相当于下表[0],end:最后一个元素的下一个,哨兵作用。如果iter指向第一个元素,*iterivec[0]指向同一个元素。++iter指向下一个元素。

15、        For (vector::iteratoriter = ivec.begin();iter!=ivec.end();++iter) *iter =0;

16、        容器的Const_iterator的类型只能读取容器内元素,但不能改变其值。

17、        任何改变vector长度的操作都会使已存在的迭代器失效。比如,在调用push_back之后,就不能信赖指向vector的迭代器的值了。

18、        Bitset类是一种类模板,与vector不一样的是bitset类型对象的区别仅在其长度而不在其类型。Bitset<32> bitvec;

19、        String对象和bitset对象之间是反向转化的:string对象的最右边字符(即下标最大的那个字符)用来初始化bitset对象的低阶位(即下标为0的位)。

第四章    数组和指针

1、 现代C++程序应尽量使用vector和迭代器类型,只有强调速度时才在类实现内部使用数组和指针。

2、 数组的大小固定,引用没有数组。非const变量以及要到运行阶段才知道其值的const变量都不能用于定义数组的维数。

3、 Char ca1[]={‘c’,’+’,’+’}  长度为3  charca2[]= “c++” 长度为4 a2[3]=’\0’

4、 vector不同,一个数组不能用另一个数组初始化,也不能将一个数组赋值给另一个数组。

5、 Size_t类型定义在cstddef头文件中,是一个与机器相关的unsighed类型,其大小足以保证存储内存中对象的大小。

6、 避免使用未初始化的指针。

7、 Void*指针可以保存任何类型对象的地址。Void *pv=&obj

8、 如果对左操作数进行解引用,则修改的是指针所指对象的值,*p=a; 如果没有使用解引用操作,则修改的是指针本身的值。

9、 引用是对象的“别名”,指针是指向对象。定义引用时必须初始化。给引用赋值修改的是该引用所关联的对象的值,而并不是使引用与另一个对象关联。

10、        **操作符指派一个指针指向另一指针。

11、        C++允许计算数组或对象的超出末端的地址,但不允许对此地址进行解引用操作。

12、        指向const对象的指针 const double *cptr。不能使用指向const对象的指针修改基础对象,但是可以改变cptr指向的const对象。

13、        Const指针Int *const curErr=&errNumb。不能改变指向对象

14、        C风格字符是以空字符null结束的字符数组。永远不要忘记字符串结束符null。建议使用标准库类型string而不是C风格字符串。

15、        数组类型的变量有三个重要的限制:数组长度固定不变,在编译时必须知道长度,数组只在它的块语句存在。动态数组则不同,它一直存在于堆中,直至程序显示释放它为止。C++使用newdelete []表达式实现这一功能。

16、        数组变量通过指定类型、数组名和维数来定义。而动态分配数组时,只需指定类型和数组长度,不必为数组对象命名,new表达式返回指向新分配数组的第一个元素的指针:int *pia= newint[10];。

17、        Strlen返回的是字符串的长度没,并不包括字符串的结束符。

18、        数组指针的定义:元素类型 变量名字 维数,int *ip[4] = ia:数组的名字其实是指针,因此需在标识符前加上*如果从内向外读ip的声明,则可理解为:*ipint[4]类型,即ip是指向含有4个元素的数组的指针。

第五章    表达式

1、 逻辑与和逻辑或先计算其左操作数,然后再计算右操作作数,若其一不符合不再计算。

2、 尽量使用if(val) , 避免使用if(val == true)。因为val不一定为bool类型,而ture1

3、 尽量使用前置操作符++i,只有必要时才使用后置操作符。

4、 *指针).对象    等价于   指针->对象。

5、 Delete p,删除指针后,该指针变成悬垂指针,应立即将指针置为0,这样就非常清楚地表明指针不再指向任何对象。

6、 强制转换?

第六章    语句

1、 一个在控制结构里引入的名字是该语句的局部变量,其作用域限在语句内部。

2、 switch语句中,每个case 需要跟一个break。程序从符合case的语句开始,并跨越case边界继续执行其他语句,直至switch结束或遇到break语句为止。

3、 Default分支后面如果没有要完成的任务,必须有一个空语句。Case 后的标号必须是整型常量表达式。对于switch结构,只能在它的最后一个case标号或default标号后定义变量。

第七章    函数

1、 非引用形参(包括指针)表示对应实参的局部副本。对这类形参的修改仅仅改变了局部副本的值。一旦函数执行结束,这些局部变量的值也就没有了????  P200

2、 指针形参调用,不改变指向的对象,但可以改变指向对象的值。

3、 如果使用引用形参的唯一目的是避免复制实参,则应将形参定义为const引用。

4、 const引用形参只能与完全同类型的非const对象关联。

5、 指向指针的引用  int *&v1

6、 返回非引用类型: 副本;返回引用:本身。不能返回局部对象的引用或指针。

7、 函数声明格式:返回类型函数名 形参列表,形参列表必须包括形参类型,但是不必对形参命名。

8、 静态局部对象 static,当定义静态局部对象的函数结束时静态局部对象不会撤销 。

9、 内联函数避免函数调用的开销,inline。内联函数应该在头文件中定义。

10、        在调用成员函数时,形参this初始化为调用函数的对象的地址。

11、        类内定义的成员函数是内联函数,类外定义必须指明它是类的成员。类名::函数名。

12、        构造函数和类同名,而且没有返回类型。

13、        Const成员函数不能改变调用该函数的对象的数据成员。在冒号和花括号之间的代码称为构造函数的初始化列表。成员名(初始值)。

14、        重载函数:出现在相同的作用域,具有相同的名字形参表不同Main函数不能重载。函数不能仅仅通过基于不同的返回类型而实现重载。

15、        在函数声明时候,形参名只是帮助文档,并没有修改形参表。形参与const形参的等价性仅适用于非引用形参,因为函数只操纵副本,无法修改实参。有const引用形参的函数与有非const引用形参的函数是不同的。

16、        指向函数的指针:bool (*p)(const string&,const string &)pf声明为指向函数的指针。用typedef简化指针的定义:

Typedef bool (*cmpFcn)(const string &,const string&) ;

bool lengthCompare (const string &,const string &);

CmpFcn pf = lengthCompare;

Pf(hi”,”bye”) ; //通过指针调用函数  也可以(*pf(“hi”,”bye”);

第八章    标准IO

1、 iostream定义读写控制窗口的类型,fstream定义读写已命名文件的类型,sstream定义用于读写存储在内存中的string对象的类型。

2、 IO对象不可复制或赋值。形参或返回类型也不能为流类型,可以使用指针或引用。

3、 条件状态:系统级的故障,无法恢复的读写错误:cin.bad()。可恢复的错误cin.fail()。文件结束符cin.eof()

4、 判断语句中的逗号操作符,首先计算它的每一个操作数,然后返回最右边操作数作为整个操作的结果。

5、 输出缓冲区的内容被刷新,即写入到真实的输出设备或者文件。比如endl

6、 在调试程序时必须保证期待写入的每次输出都确实被刷新。所有的输出操作都显示的调用flushendl

7、 任何输入流的尝试都将首先刷新其输出流关联的缓冲区。

8、 需要读写文件时,则必须定义自己的对象,并将它们绑定到需要的文件上。Ifstream infile(ifile.c_str()); //ifile为文件名,  读文件 

9、 如果调用open或使用文件名作为初始化式,需要传递的实参应该为C风格字符串,可以通过c_str函数,把string类型转化为C风格字符串。

10、        Fstream对象一旦打开,就保持与指定的文件相关联。如果要把fstream对象与另一个不同的文件关联,则必须先关闭(close)现在的文件,然后打开(open)另一文件:在尝试打开新文件前,必须先关闭当前的文件流。

11、        如果程序员需要重用文件流读写多个文件,必须在读另一个文件之前调用clear清楚该流的状态。

12、        模式(inoutappatetruncbinary)是文件的属性而不是流的属性。

13、        Stringstream对象的使用。先读入每行,然后读入每行中的每一个单词。        P257

String line,word;

While(getline(cin,line)){

Istringstream stream(line);

While(stream>>word){

        ….

}

}

第二部分 容器和算法

第九章    顺序容器

1、 容器的比较P322。使用元素比较。

2、 Resize操作可能会使迭代器失效。

3、 Beginend成员返回一个迭代器,它指向第一个元素和最后一个元素的下一个位置frontback成员返回一个元素的引用,第一个元素的引用和最后一个元素的引用。

4、 顺序容器中添加元素:push_back,push_front,insertP273。删除元素pop_back,pop_front等。P280.

5、 Vector容器不支持pop_front操作。此类poppush返回void

6、 Pop_front操作通常与front操作配合使用,实现栈。While(!ilist.empty()){process(ilist.front()):ilist.pop_front();}

7、 C.assign(b,e): 重置c的元素,将迭代器be标记的范围内所有的元素复制到c中。Be必须不是指向c中元素的迭代器。Assign操作首先删除容器中所有元素,然后将其参数所指定的新元素插入到该容器中。

8、带有一对迭代器参数的assign操作,不要求相同容器,只要元素类型相互兼容就可以。

9、Swap操作实现交换两个容器内所有元素的功能。要求:同一类容器,元素类型相同。执行swap操作后,迭代器不失效,指向同一个元素。

10、        Capacity(容量)操作获取在容器需要分配更多的存储空间之前能够存储的元素总和。Reserve操作告诉vector容器应该预留多少个元素的存储空间。

11、        Vectordeque是顺序存储,list是链式存储。

12、        通常来说,除非找到选择其他容器的更好理由,否则vector容器都是最佳选择

13、        再谈string P289

14、        Stack适配器所关联的基础容器可以是vectorlist或者dequequeue适配器需要push_front运算,因此只能建立在list容器上;priority_queue适配器要求提供随机访问功能,因此可建立在vectordeque容器上。

15、        默认stackqueue都是基于dequepriority_queue则在vector容器上实现。

16、        本质上,适配器是使一事物的行为类似于另一事物的行为的一种机制。

第十章    关联容器

1、 关联容器支持通过键来高效地查找和读取元素。两个基本的关联容器类型是mapset

2、 Pair类型,在utility头文件中定义:pair p1 T1T2的类型不要求相同。Pair类型的使用相当频繁,可以考虑利用typedef简化其声明。

3、 Pair的成员firstsecond都是公开的,只需要点操作符就可以访问。

4、 除了构造函数,也可以使用make_pair函数创建新的pair对象。

5、 关联容器不提供frontpush_frontpop_frontbackpush_backpop_back操作。

6、 关联容器的erase操作返回void类型,顺序容器返回一个迭代器,指向被删除后面的一个元素。

7、 Map是键-值对的集合。在定义map对象时,必须分别指明键和值的类型:map word_count.

8、 对于键类型,唯一的约束就是必须支持<操作符,至于是否支持其他的关系或者相等运算,则不作要求。而且键为const

9、 Map的元素类型(value_type)是pair类型,它的值成员可以修改,但键成员不能修改。此外还有key_valuemapped_type以获取键和值的类型。

10、        Map容器,一个给定的键只能对应于一个元素,这一事实影响了map添加元素的操作。

11、        使用下标访问map与使用下标访问数组或vector的行为截然不同:用下标访问不存在的元素将导致在map容器中添加一个新的元素,它的键即为该下标值。

12、        M.insert(e):em中,则保持m不变。

13、        带有一个键-pair形参的insert版本将返回一个值:<迭代器,bool>pair。迭代器指向map中具有相应键的元素,而bool值则表示是否插入了该元素,成功插入返回true

14、        使用下标读取map中的元素有一个很危险的副作用:如果该键不在map容器中,那么下标操作会插入一个具有该键的新元素

15、        Map提供countfind操作,用于检查谋个键是否存在而不会插入该键。P367

16、        map对象中删除元素:erase操作。Map容器的erase操作返回void,而顺序容器的erase操作则返回一个迭代器,指向被删除元素后面的元素。Map类型还提供一种额外的erase操作,其参数是key_type类型的值(键),成功删除返回1

17、        Map容器同样提供beginend运算,以生成用于遍历整个容器的迭代器。

18、        读取单词。String line;getline(input,line);istringstream stream(line); string word;stream>>word;

19、        Map容器是键-值对的集合;而set容器只是单纯键的集合。所谓键的集合,set容器存储的键必须唯一且不能修改

20、        Set容器不提供下标操作符。

21、        Mapset容器中,一个键只能应对一个实例。而multisetmultimap类型则允许一个键对应多个实例Multimap不支持下标操作。使用它们时,对于谋个键,必须做好处理多个值的准备,而非只有单一的值。

22、        关联容器mapset的元素是按顺序存储的。Multimapmultiset容器中,对于某个键对应多个实例,则这些实例在容器中将相邻存放

23、        查找元素:1、使用findcount操作;2lower_bound(第一个)upper_bound(最后一个的下一个)操作,这两个操作不会说明键是否存在,其关键之处在于返回值给出了迭代器范围;3equal_range返回一对迭代器的pair对象。

第十一章 泛型算法

1、 泛型算法:可作用在不同类型的容器和不同类型的元素上。

2、 在涉及写入元素的算法中,必须确保算法所写入的序列至少足以存储要写入的元素。

3、 确保算法有足够的元素存储输出数据的一种方法是使用插入迭代器Back_inserter函数是迭代器适配器,使用一个对象作为实参,生成一个适应其实参行为的新对象。P342

4、 通常,如果要以一个已存在的容器为副本创建新容器,更好的方法是直接用输入范围作为新构造容器的初始化式:vector ivec(ilst.bgein().ilst.end());

5、 替换函数如果不想改变原来的序列,则调用replace_copy,接受第三个迭代器实参,指定保持调整后序列的目标位置。Vectorvec;replace_copy(ilst.begin().ilst.end(),back_inserter(ivec),0,42)

6、 去除重复unique函数,并不删除重复的数据而把它们放在序列的最后,返回最后不重复元素的下一个位置的迭代器。

7、 谓词是做某些检测的函数,返回用于条件判断的类型,指出条件是否成立。

8、 Sort函数和stable_sort函数。P345

9、 插入迭代器:这类迭代器与容器绑定在一起,实现在容器中插入元素的功能。插入器是一种迭代器适配器,带有一个容器参数,并生成一个迭代器,用于在指定容器中插入元素。三种插入器,其区别在于插入元素的位置不同。back_inserterfront_inserter,inserter

10、        Vector不提供push_front,故不能使用front_inserter

11、        Front_inserterinsert有很大的区别,前者在首位插入,可以生成倒序,而后者在固定位置插入。

12、        Ostream_iterator对象必须与特定的流绑定在一起。而istream_iterator创建时可以不提供实参,则该迭代器指向超出末端位置。

13、        使用istream_iterator对象将标准读入到vector对象中。P350

Istream_iterator in_iter(cin);

Istream_iterator eof;

Vector vec(in_iter,eof);

14、        反向迭代器是一种反向遍历容器的迭代器。++运算将访问前一个元素。Rend()指向第一个的前一个,rbegin()指向最后一个。

15、        反转时可以使用base成员函数。

16、        Mapsetlist提供双向迭代器;string,vector,dequeue提供随机访问迭代器,istream_iterator输入迭代器;ostream_iterator输出迭代器。

17、        检查指定的算法默认使用==操作符。系统为这类算法提供另外命名的版本,带有谓词函数形参。带有谓词形参的算法,起名字后缀为_if,find_if(beg,end,pred),查找使pred0的元素。

18、        复制版本,_copyreverse_copy(beg,end,dest),复制输入序列的元素,并将他们以逆序存储到dest开始的序列中。

19、        List容器上的迭代器是双向的,而不是随机访问类型。

20、        List容器特有算法removeunique真正删除了指定的元素。Mergesplice运算会破坏他们的实参。

第三部分 类和数据抽象

1、 类定义了数据成员和函数成员:数据成员用于存储与该类类型的对象相关联的状态,而函数成员则负责执行赋予数据意义的操作。通过类能够将实现和接口分离。

2、 类类型常称为抽象数据类型。抽象数据类型将数据(即状态)和作用于状态的操作视为一个单元。

3、 C++中的类能够控制在初始化、复制、赋值和销毁对象时发生的操作。

4、 利用操作符重载,在C++中创建新的类型,就像创建内置类型一样。

5、 转换函数(一种特殊的成员函数)定义了类类型对象之间的隐式转换。

第十二章 类

1、 C++中,通过类定义自己的抽象数据类型。

2、 数据抽象能够隐藏对象的内部表示,同时仍然允许执行对象的公有操作。

3、 构造函数初始化列表。P369

4、 在类内部,声明成员函数是必需的,而定义成员函数是可选的。在类内定义的函数默认为inline。在类外部定义的成员函数必须指明它们是在类的作用域中。

5、 将关键字const加在形参表之后,就可以将成员函数声明为常量,const成员不能改变其操作的对象的数据成员

6、 类的背后蕴涵的基本思想是数据抽象封装。数据抽象是一种依赖于接口和实现分离的编程技术。封装是一种将低层次的元素组合起来形成新的、高层次实体的技术。

7、 c++中,使用访问标号来定义类的抽象接口和实施封装。

8、 保持类的使用者设计者的区分是有益的。设计类的接口时,设计者应该考虑的是如何方便类的使用;使用类的时候,使用者就不应该考虑类如何工作。

9、 数据抽象和封装提供了两个重要优点:

1) 避免类内部出现无意的、可能破坏对象状态的用户级错误

2) 随着时间推移可以根据需求改变或缺陷报告来完善类实现,而无须改变用户级代码

10、        别名可以在类的public中定义。

11、        在声明和定义处指定inline都是合法的。在类的外部定义inline的一个好处是可以使得类比较容易阅读。

12、        不完全类型:类声明之后,定义之前。不能定义该类型的对象,不完全类型只能用于定义指向该类型的指针及引用,或用于声明(而不是定义)使用该类型作为形参类型或返回类型的函数。

13、        给类定义对象,需要给对象预定相应的存储空间。类不能具有自身类型的数据成员,但是可以有指向自身类型的指针或引用。

14、        返回*this, 返回一个引用,该引用指向执行操作的那个对象。Screen&

15、        const普通成员函数返回指向类对象的普通引用Screen&Const成员函数只能返回const引用,const Screen&Const指针可以改变指针指向的对象,但不能改变指向的对象,即保存的地址不能变;const类类对象的const指针既不能改变指向的对象,也不能改变保存的地址。

16、        有时,我们希望类的数据成员(甚至在const成员函数内)可以修改。这可以通过将它们声明为mutable来实现。

17、        在类作用域之外,成员只能通过对象或指针分别使用成员访问操作符.->来访问。

18、        形参表和函数体处于类作用域中;函数返回类型不一定在类作用域中。

19、        类定义:首先编译成员生命;然后在所有成员出现之后,才编译他们的定义本身。

20、        尽管全局对象会被屏蔽,但通过全局作用域确定操作符来限定名字,仍可以使用它。如, ::height

21、        只要创建类类型的新对象,都要执行构造函数。构造函数的工作是保证每个对象的数据成员具有合适的初始值。构造函数与类的名字相同,并且不能指定返回类型。

22、        构造函数分两个阶段执行:(1初始化阶段(初始化列表),(2)普通计算阶段(函数体赋值)。

23、        在执行构造函数整体之前,要完成初始化。初始const或引用类型以及没有默认构造函数的类类型的数据成员必须在构造函数初始化列表中

24、        建议使用构造函数初始化表。

25、        成员初始化的次序是定义成员的次序

26、        当定义一个类,没有定义默认构造函数时,编译器会自动生成一个默认构造函数;若定义了任意一种构造函数,编译器将不会再生成默认构造函数

27、        如果类包含内置或复合类型的成员,应该定义自己的构造函数来初始化这些成员。内置和复合类型只对定义在全局作用域中的对象初始化,

28、        类通常应定义一个默认构造函数。

29、        可以用单个实参来调用的构造函数定义了从形参类型到该类类型的隐式转换。可以通过explicit,来防止在需要隐式转换的上下文中使用构造函数。Explicit关键字只能用于构造函数声明上。一般,单形参构造函数应该为explicit

30、        友元(friend)机制允许一个类对其非公有成员的访问权授予指定的函数或类。P397

31、        当我们将成员函数声明为友元时,函数名必须用该函数所属的类名字加以限定。

32、        Static数据成员独立于该类的任意对象而存在,每个static数据成员是与类关联的对象,并不与该类的对象相关联。

33、        Static函数没有this指针。Static成员不是任何对象的组成部分,所以static成员函数不能被声明为const。也不能声明为虚函数。

34、        Static数据成员必须在类定义体的外部定义。在这个规则的一个例外是,只要初始化式是一个常量表达式。

第十三章 复制控制

1、 复制构造函数赋值操作析构函数总称为复制控制。编译器自动实现这些操作,但类也可以定义自己的版本。类具有指针成员时,需要自定义

2、 复制构造函数:只有单个形参,而且形参是对本类类型对象的引用(常用const修饰),这样的构造函数。

3、 C++支持两种初始化形式:直接初始化()和复制初始化=。前者直接调用与实参匹配的构造函数,后者调用复制构造函数。

4、 复制构造函数一般都设置为explicit

5、 一般需要定义复制构造函数的类:一种,有数据成员是指针或有成员表示在构造函数中分配的其他资源。另一种,类在创建新对象时必须做一些特定工作。

6、 为了防止复制,类必须显式声明其复制构造函数为private。大多数类应定义复制构造函数和默认构造函数。

7、 复制操作符的右操作数一般作为const引用传递。返回类型是右操作数的引用。如:

Sale_item& operator=(const Sale_item &);

8、 复制和复制常一起使用。一般而言,如果类需要复制构造函数,它也会需要赋值操作符。

9、 构造函数的一个用途是自动获取资源。而析构函数则完成所需资源回收,作为类构造函数的补充。

10、        三法则:复制构造函数、赋值操作和析构函数一般共进退。

11、        析构函数不仅限于用于释放资源。一般而言,析构函数可以执行任意操作,该操作是类设计者希望在该类对象的使用完毕之后执行的。动态分配的对象只有在指向该对象的指针被删除时才撤销

12、        合成析构函数并不删除指针成员所指向的对象

13、        析构函数与复制构造函数或赋值操作符之间的一个重要区别是,即使我们编写了自己的析构函数,合成析构函数仍然运行。先执行自定义,在执行合成析构函数。

14、        只有删除指向动态分配对象的指针实际对象(而不是对象的引用)超出作用域时,才执行析构函数。

15、        包含指针的类需要特别注意复制控制,原因是复制指针只复制指针中的地址,而不会复制指针所指向的对象

16、        使用默认合成复制构造函数,指针成员指向的对象是共享的,可能发生悬垂指针

17、        悬垂指针解决方法:定义智能指针可引用使用计数(使用计数类),或者定义值型类(指针指向对象的副本)

第十四章 重载操作符与转换

1、 重载操作符必须具有至少一个类类型或枚举类型的操作数。

2、 重载操作符并不保证操作数的求值顺序。

3、 作为类成员的重载函数,其形参看起来比操作数数目少1。作为成员函数的操作符有一个隐含的this形参,限定为第一个操作数。

4、 一般将算术和关系操作符定义为非成员函数,而赋值操作符定义为成员。设置为非成员函数时,通常必须将他们设置为所操作类的友员。

5、 重载逗号、取地址、逻辑与、逻辑或等操作符通常不是好做法。会破坏他们的内置意义。逗号操作符从左到右计算每个表达式的值,并返回最右边操作数的值。

6、 赋值、下标、调用和成员访问箭头等操作符必须定义为成员。改变对象状态与给定类型紧密联系的其他一些操作符,如自增、自减和解引用通常定义为类成员。对称的操作符,如算术操作符、相等操作符、关系操作符和位操作符最好定义为普通非成员函数。

7、 输出操作符<<的重载:

为了与IO标准库一致,操作符应接受ostream&作为第一个参数(ostream不允许复制),对应类型const对象的引用作为第二个参数(const一般不变,引用避免复制实参),并返回对ostream形参的引用(通常是输出操作符所操作的ostream)。

8、 输出操作符通常所做格式化应尽量少,不应该输出换行符。

9、 IO操作符必须为非成员函数。

10、        输入操作符必须处理错误和文件结束的可能性。设计输入操作符时,如果可能,要确定错误恢复措施,这很重要。有时输入操作符还需要设置failbit

11、        为了与内置操作符一致,加法返回一个右值,而不是一个引用。一般根据复合赋值操作符(如+=)来实现算术操作符(如+)。

12、        赋值操作符必须是类的成员。一般而言,赋值操作符与复合赋值操作符应返回左操作数的引用。

13、        类定义下标操作符是,一般需要定义两个版本:一个为非const成员并返回引用,另一个为const成员并返回const引用。

14、        箭头操作符与众不同,它不接受显示形参。重载箭头操作符必须返回指向类类型的指针,或者返回定义了自己的箭头操作符的类类型对象。

15、        可以为类类型的对象重载函数调用操作符()。函数调用操作符必须声明为成员函数。定义了调用操作符的类,其对象常称为函数对象。函数对象:对象的使用类似于函数

16、        每个函数对象类都是一个类模板,我们需要为该模板提供一个类型。函数对象类的模板类型指定调用操作符的形参类型。

17、        可用一个实参调用的非explicit构造函数定义一个隐式转换。当提供了实参类型的对象而需要一个类类型的对象时,编译器将使用该转换。除此之外,我们还可以定义转换操作符,给定类类型的对象,该操作符将产生其他类型的对象。

18、        转换操作符定义在类定义体内声明,在保留字operator之后跟着转换的目标类型:

Operator int() const{ return val;}转换函数一般不应该改变被转换的对象。因此,转换操作符通常应定义为const成员。

19、        只要存在转换,编译器将在可以使用内置转换的地方自动调用它。

20、        类类型转换之后不能再跟另一个类类型转换。如果需要多个类类型转换,则代码将出错。例如,假定有一个类Integral,它可以转换为SmallInt但不能转换为int

21、        接受单个形参且未指定为explicit的构造函数定义了从其他类型类类型的转换,重载操作符转换函数则定义了从类类型其他类型的转换。

第四部分 面向对象编程与泛型编程

第十五章 面向对象编程

1、 面向对象编程基于三个基本概念:数据抽象继承动态绑定。在C++中,用类进行数据抽象,用类派生从一个类继承另一个类:派生类继承基类的成员。动态绑定使编译器能够在运行时决定是使用基类中定义的函数还是派生类中定义的函数。

2、 C++中,基类必须指出希望派生类重定义函数为virtual虚函数。

3、 通过动态绑定我们能够编写程序使用继承层次中任意类型的对象,无须关心对象的具体类型。比如形参可以为基类,而实参可以是基类或基类的派生类,而实际调用的函数将在运行时确定。

4、 C++中,通过基类的引用(或指针)调用虚函数时,发生动态绑定。引用(或指针)既可以指向基类对象也可以指向派生类对象,这一事实是动态绑定的关键。用引用(或指针)调用虚函数在运行时确定,被调用的函数是引用(或指针)所指对象的实际类型所定义的

5、 基类一般都定义虚析构函数。

6、 保留字virtual的目的是启动动态绑定。只能出现在类内部的成员函数声明中。基类通常应将派生类需要重定义的任意函数定义为虚函数。

7、 Publicprivate的访问规则对派生类同样适用。但是,有时作为基类的类具有一些成员,它希望允许派生类访问但仍禁止其他用户访问这些成员。对于这些成员应使用受保护的访问标号。Protected成员可以被派生类对象访问但不能被该类型的普通用户访问。

8、 可以认为protected访问标号是privatepublic的混合:

1) private成员一样,protected成员不能被类的用户访问。

2) public成员一样,protected成员可以被该类的派生类访问。

3) 派生类只能通过派生类对象访问其基类的protected成员,派生类对其基类类型对象的protected成员没有特殊访问权限???P475

9、 提供给派生类型的接口是protectedpublic成员的组合。

10、        派生类列表:class classname:access-label base-class  

11、        派生类继承基类的成员并且可以定义自己的附加成员。每个派生类对象包含两部分:从基类继承的成员和自己定义的成员。

12、        派生类型必须对想要重定义的每个继承成员进行声明。并且派生类中虚函数的声明必须与基类中的定义方式完全匹配,但有一个例外:返回对基类型的引用(或指针)的虚函数。派生类中的虚函数可以返回基类函数所返回类型的派生类的引用(或指针)。

13、        一旦函数在基类中声明为虚函数,它就一直为虚函数,派生类无法改变该函数为虚函数这一事实。派生类重定义虚函数时,可以使用virtual保留字,但不是必须这样做。

14、        因为每个派生类对象都有基类部分,类可以访问其基类的publicprotected成员,就好像那些成员是派生类自己的成员一样。基类的private不可以

15、        动态绑定必须满足两个条件:

1)    只有指定为虚函数的成员函数才能进行动态绑定。

2)    必须通过基类类型的引用或指针进行函数调用。

16、        因为每个派生类对象都包含基类部分,所以可以将基类类型的引用绑定到派生类对象的基类部分,也可以用指向基类的指针指向派生类对象。如果调用非虚函数,则无论实际对象是什么类型,都执行基类类型所定义的函数。如果是调用虚函数,则直到运行时才能确定调用哪个函数,运行的虚函数是引用所绑定的或指针所指向的对象所属类型定义的版本。

17、        在某些情况下,希望覆盖函数机制并强制函数调用使用虚函数的特定版本,这时可以使用作用域操作符如:p->Item_base::net_price(42);

18、        派生类虚函数调用基类版本时,必须显示使用作用域操作符。如果派生类函数忽略了这样做,则函数调用会在运行时确定并且将是一个自身调用,从而导致无穷递归。

19、        默认实参与对象的动态性无关。通过基类的引用或指针调用虚函数时,默认实参为在基类虚函数声明中指定的值,如果通过派生类的指针或引用调用虚函数,则默认实参是在派生类的版本中声明的值。

20、        对类所继承的成员的访问由基类中的成员访问级别派生类派生列表中使用的访问标号共同控制。每个类控制它所定义的访问。派生类可以进一步限制但不能放松对所继承的成员的访问。

21、        无论派生列表中是什么访问标号,所有继承基类的类对基类中的成员具有相同的访问。派生访问标号将控制派生类的用户对从基类继承而来的成员的访问

22、        友元关系不能继承。

23、        存在派生类型引用到基类类型引用的自动转换,不存在从基类引用到派生类引用的自动转换。一般可以使用派生类型对象对基类对象进行赋值或初始化。

24、        对基类对象进行初始化或赋值,实际上是在调用函数:初始化时调用构造函数,赋值时调用赋值操作符。

25、        基类一般定义自己的复制构造函数和赋值操作符,这些成员接受一个形参,该形参是基类类型的const引用。因为存在从派生类引用到基类引用的转换,这些复制控制成员可用于从派生类对象对基类对象进行初始化或赋值。

26、         

你可能感兴趣的:(C++note)