-
第一章-开始
- 默认情况下cerr的数据不缓冲,直接输出,所以也无法重定向(p23)
- clog和cout的数据是会缓冲的(p23)
-
第二章-变量和基本类型
- C++基本数据类型有:算数类型和空类型(p30)
- 长度问题:书上写的都是为最小尺寸,一般VC的int为32位,long也为32位(p30)
- 一个表达式中既有无符号数又有有符号数,通常将有符合数转化为无符号数(p34)
- 以0(零)开头的整数代表8进制,0x开头的整数代表16进制(p35)
- 字符串字面值的实际长度通常比它的内容多1,如果两个字符串字面值位置紧邻,且仅有空格、缩进和换行,它们实际上是一个整体(p36)
- 使用列表初始化和赋值时,当存在信息丢失风险时,编译器会报错(p39)
- 定义于任何函数体之外的变量被初始化为0,类对象中的成员变量也被初始化为0。定义在函数体内部的内置类型变量不被初始化(p40)
- 变量只可以被定义一次,但可以被多次声明,关键字extern(p41)
- $符可以用于标识符,和_等价,_可以单独作为变量名(p42)
- 多个文件间共享const,可以在变量的定义前加extern(p54)
- 初始化常量引用时允许任意表达式作为初始值,只要存在隐式转换即可,发生转换时,产生临时量,常量引用绑定的是临时量(p55)
- 函数体内部的变量一般是并非存放在固定地址,因此constexpr指针不能指向这样的变量(p59)
- typedef定义指针时,在加const容易错误理解。typedef char* p; 这里的p的类型是指针,const p pstr=0; pstr 的类型为 char * const。p(61)
- auto 关键字往往会忽略顶层const(p62)
- decltype的表达式加上括号,结果永远为引用类型(p63)
-
第三章-字符串、向量和数组
- 头文件中不建议包含using(p75)
- C++中的字符串字面值(const char [] 类型)和string对象不是同一类型(p81)
- 确认无法执行列表初始化后,编译器会尝试用默认值初始化vector对象(p89)
- 范围for循环语句体内不应改变其所遍历序列大小(p91)
- 内置的下标运算符号支持负数(并不是负向索引),和string、vector不同(p108)
- 数组名是不可修改的左值
-
第四章-表达式
- 函数调用也是一种特殊的运算符(p120)
- 对象作用右值时,用的是对象的值,作用于左值是对象的身份(p121)
- 对于未定义执行顺序的运算符应避免修改同一对象及类似操作,如"<<"操作符。&&操作符先求左侧,左侧为真时才求右侧(p123)
- 后置递增运算符优先级高于解引用运算符,例:"*p++"(p132)
- const_cast只能改变运算对象的底层const(const int a;是顶层const)(p145)
- reinterpret_cast通常为运算对象的位模式提供较低层次的重新解释(p145)
-
第五章-语句
- switch内部变量注意不要跳过变量的声明,可以加域来防止出现错误(p164)
- for(declaration:expression) statement 是C++11引入的(p168)
- goto语句在C++中是可以使用的,只是不建议使用(p172)
-
第六章-函数
- 函数调用:1.用实参初始化形参 2.控制权转移到被调函数(p182)
- 函数返回:1.返回值(如果有) 2.控制权从被调函数转移回主调函数(p183)
- 实参求值顺序未确定,不要传入有关系的运算表达式,如func(i,i++,++i)(p184)
- 如果一个函数永远不会被用到,可以只声明,没定义(p186)
- 引用传递与值传递、传指针属于值传递(p188)
- 形参初始化方式类似变量初始化,实参初始化形参会忽略顶层const,之所以忽略是因为底层const可以根据判断所指向或引用的是否为常量而区分,不带底层const的指针或引用是无法指向和引用常量的(p191)
- 不允许拷贝数组(p196)
- initializer_list<T> 属于标准库,其中元素永远是常量,实参:{ } (p198)
- 省略符形参仅仅用于C和C++通用类型,大多数类类型无法正确拷贝(p199)
- 返回一个值的方式和初始化一个变量或形参的方式一样(p201)
- 不要返回局部对象的引用或指针(p201)
- C++11规定,函数可以返回花括号包围的值列表,词列表用来表示函数返回的临时量初始化(p203)
- main函数不能调用自己(p204)也不能重载(p207)
- Type (*function(parameter_list))[dimension] 返回数组指针(p205)
- 也可以使用尾置返回类型auto function(parameter_list)->type(*)[dimension]返回数组指针(p206)
- 顶层const不影响传入的函数对象,因此无法构成重载(可能是因为实参初始化会忽略顶层const)(p208)
- 作用域内声明函数会隐藏掉外层的函数声明,即时参数不同,也不会重载(p210)
- 缺省参数只能放在参数列表尾部,注意重载(p212)
- 默认实参可在不同的声明中有不同的默认值,不过不可以在一个作用域内声明多次(p212)
- 局部变量不能作为默认实参(p212)
- constexpr函数隐式内联,若函数返回值在编译期间确定为常量则constexpr返回常量表达式,若不是就当没有constexpr修饰符(p214)
- 若定义了宏NDEBUG则assert() 失效(p216)
- 当有多个形参的函数匹配时:
- 该函数每个实参的匹配都不劣于其它可行函数需要的匹配
- 至少有一个实参的匹配优于其它的可行函数提供的匹配
- 若出现冲突,说明有二义性
- 函数类型由其返回类型和形参类型共同决定,和函数名无关(p221)
- 可以直接把函数作为实参使用,此时它会自动转化为指针(p222)
-
第七章-类
- 类的基本思想是数据抽象和封装,数据抽象依赖于接口和实现(p228)
- this指针是一个常量指针,紧跟在函数后的const表示this指向一个常量,这也就解释了为什么在常量成员函数中无法修改类的成员变量p(231,232)
- 一般来说类的相关非成员函数的声明和类的头文件定义在一起(p234)
- 只有类没有声明任何构造函数时,编译器才会自动生成默认构造函数(p236)
- 关于类的初始化,如果类内有内置类型或符合类型,那么类的默认构造函数应当尽量保证这些变量被初始化(p236)
- 使用冒号括号来进行初始化的构造函数会覆盖掉变量的类内初始值(p238)
- 局部对象会在创建它的块结束时被销毁,vector对象或数组销毁时,存储在其中的对象也会被销毁(p239)
- 某些类不能依赖于合成的版本,尤其是类需要分配类对象外的资源,比如包含动态申请内存的类,可以使用string,vector来减少这些麻烦(p239)
- C++中使用struct和class定义类的唯一区别就是默认访问权限不同(p241)
- 类想要其它类或者函数访问它的非共有成员函数,可以使其它类或函数成为它的友元,只需增加一条其它类或函数的以friend开头的函数声明即可(p241)
- 友元声明仅仅指定了访问权限,若想使用该函数,还应当再次声明(p242)
- 用来定义类型的成员必须先定义后使用(p243)
- 类内成员函数默认内联,类外可以使用inline关键字显示指定,inline成员函数应当和相应的类定义在同一个头文件中(p244)
- 关键字mutable修饰一个可变的数据成员,即时在const函数中也可以修改其值(p245)
- 重载const,调用时取决于,调用对象是否为常量(p248)
- 对于一个类类型,声明后定义前是一个不完全类型,不完全类型只能定义指针或引用(p250)
- 友元是没有传递性的,没有继承关系,每个类只负责自己的友元类或函数(p251)
- 成员函数可以做友元,遵循规则:1.定义类A,声明函数fun。2.定义类B,声明友元函数fun。3.定义函数fun(p252)
- 即使在类内声明,甚至定义了友元,若不在类外部声明该函数或类,无论在类内还是类外,都无法使用该友元(p252)
- 类的定义分两步,先编译成员的声明,直到类全部可见后才编译函数体(p254)
- 一般内层作用域可以重新定义外层作用域名字,即使该名字使用过,但在类的声明中并,如果使用了外部类型名字,就不能再重新定义它了(p255)
- 类成员初始化是按它们在类中出现的顺序初始化的,要注意在使用构造函数初始化类成员变量时,不要出现使用未初始化的值来为已初始化过的值初始化(p259)
- 委托构造函数,委托构造函数使用它所属类的其他构造函数执行它的初始化(p261)
- class A a();是函数声明,class A a;声明了一个对象(p263)
- 实参调用的构造函数定义了一条从构造函数的参数类型向形参的隐式转化规则,但只允许一步类型转化(p264)
- 可以使用explicit来抑制构造函数的隐式转化(p265)
- 一个聚合类的所有成员都是public的、没有定义任何构造函数、没有类内初始值、没有基类和虚函数(p266)
- 字面值常量类的数据成员都是字面值类型、至少含有一个constexpr构造函数、类成员初始值必须是常量表达式或字面值常量类、使用默认析构函数。或者是一个字面值类型的聚合类(p267)
- 静态成员函数不能声明为const的,也不能在其中显式或隐式使用this指针(p269)
- static关键字只出现在类内的声明或定义中(p270)
- 一般来说类的静态成员必须在类的外部定义和初始化,建议将其定义放在和非内联成员函数放在一起(p270)
- 只在类内定义和初始化的静态成员必须是字面值常量,仅用于在编译时可替换其值的情况,若在类内初始化则在类外定义时不能初始化(p271)
- 静态成员可以是不完整的类型(p271)
- 静态成员可以作为类成员函数的默认参数(p271)
-
第八章-IO库
- 概念上设备类型和字符大小不影响IO操作(p279)
- IO对象没有拷贝和赋值,可以通过引用传递,但不能为const,因为操作IO对象会改变其状态(p279)
- 文件结束时failbit和eofbit都会被置位(p280)
- 当badbit被置位时fail返回true(p281)
- 缓存区刷新的5原因,unitbuf可以使所有输出操作立即刷新(p282)
- 程序崩溃不会刷新缓存区(p282)
- 对一个已经打开的文件流调用open会失败,会将failbit位置true,后续对文件流的操作也会失败(p285)
- fstream对象被销毁时,close自动调用(p285)
- 文件的6种模式(p286)
-
第九章-顺序容器
- 标准库中的顺序容器有:verctor[可变大小数组],deque[双端队列],list[双向链表],forward_list[单向链表],array[固定大小数组],string[字符串](p292)
- 迭代器范围是一个左闭合区间,因为第二个迭代器通常指向尾元素之后的位置[begin,end)(p296)
- 左闭合区间的限制与假定(p297)
- begin是被重载过的,有有两个版本一个const一个不带const,cbegin是C++11新标准为了支持auto引入的(p298)
- 容器的拷贝两种方法:直接拷贝容器。拷贝迭代器范围。其中拷贝容器必须类型相同,拷贝范围则元素可转化即可(p300)
- array的大小是其一部分(p301)
- 赋值会使指向左侧容器内部的迭代器、引用、指针失效。除array和string外swap不会使指向左侧容器内部的迭代器、引用、指针失效(p302)
- 除array外,swap不对任何元素拷贝、删除、插入。swap交换的是容器内部的数据结构(p303)
- 容器中的值是传入对象的拷贝,和原值没有任何关系(p306)
- C++11版的insert在接受多个元素插入的操作,返回指向第一个新加入元素的迭代器(p308)
- C++11引入了emplace可以直接传递参数,插入元素的值,避免在形参传输时产生的临时对象(p308)
- front和back函数分别返回容器的头和尾引用,对空容器调,行为未定义(p310)
- at函数可以抛出异常,用来测试下标是否越界,front和下标若越界都是未定义的行为,引发程序崩溃,空容器使用begin也没有问题,此时begin和end相等(p310)
- 对于forward_list具有一系列的特殊操作,其中before_begin返回首前元素(p312)
- resize改变容器大小,缩小容器大小会使容器后部元素删除,增大大小会添加新的元素,若不显示制定元素值,应当保证元素类型存在默认构造函数(p314)
- 在进行容器的增删操作后可能会使迭代器失效,使用失效迭代器是未定义的(p315)
- 不要保存end返回的迭代器,尤其在循环中存在插入、删除操作时(p317)
- reserve当参数大于capacity时会重新分配内存空间,小于等于没有变化(p318)
- shrink_to_fit使capacity缩减到最小,但不一定会重新分配内存(p318)
- vector重新分配内存条件:resize或reserve超过当前capacity时、insert时size和capacity相等(p320)
- 除了接受迭代器版的insert和erase,string还提供了接受下标的insert和erase,两者都是返回字符串的引用(p322)
- 迭代器版的insert返回插入结果前的位置,erase返回删除后的位置,即
string str="hello"; auto a=str.insert(str.begin(1),5,'a'); //此时的str为"haaaaaello",a指向第一个a的位置 str.assign("world"); a=str.erase(str.begin()+1,str.begin()+2); //此时str为"wrld",a指向r的位置
- 当find找不到时返回string::npos(p327)
- compare比较和c语言中的strcmp类似(p327)
- string转数值时,参数开头第一个非空白符必须是符号(+-)和数字,可以“0x”开头表示16进制,也可以“.”开头表示浮点数,不能转化或无法表示时抛出异常(p328)
- 标准库定义了三种顺序容器适配器:stack、queue和priority_queue(p329)
- priority_queue允许为队列中元素建立优先级(p331)
-
第10章-泛型算法
- 大多数算法定义在algorithm中,标准库还在numeric中定义了一组数值泛型算法(p336)
- find算法查找两个迭代器间的元素,找到返回元素,否则返回后一个迭代器(p336)
- 迭代器使算法不依赖于容器,但算法依赖元素类型的操作,比如"比较"(p337)
- equal算法要求第二个序列的长度比第一个长(p339)
- back_inserter定义在头文件iterator中,不属于标准算法(p341)
- copy算法默认后者序列长度至少和前者相等(p341)
- unique算法使相邻的重复的元素被放到末尾,返回最后一个不重复的元素之后的位置,它和sort一样都不会改变容器大小(p343)
- 稳定排序即维持相等的元素的原有顺序,相等是相对的(p345)
- 可调用对象有:函数、函数指针、重载函数调用符的类、lambda表达式(p346)
- lambda表达式形式:[capture list](parameter list)->return type{function body}(p346)
- 当定义一个lambda表达式时,编译器生成一个与lambda对应的新的类类型(p349)
- lambda变量捕获也区分值和引用,值捕获时被捕获变量是在lambda表达式创建时拷贝的,而不是在调用时捕获(p350)
- 和普通函数引用传参一样,lambda表达式在执行时,必须确保被捕获的引用对象是存在的(p350)
- 隐式捕获的是捕获函数参数列表,可以使用【&】表示引用,【=】表示值(p352)
- 值捕获变量默认不可更改,可以通过添加mutable关键字表明捕获变量可以修改,此时修改捕获的变量对外界同样没有任何影响(p352)
- 引用捕获变量和普通传参一样,该const的带const(p353)
- p(353)中的指定lambda返回类型,书上的示例测试时即使没有出错,可能已经修改了,只要记住不能自动推断想要的类型,自己显示指出就行(p353)
- lamdba表达式和函数有各自的适用范围(p354)
- bind定义在头文件functional中,auto newCallable=bind(callable,arg_list); bind函数通过绑定一个可调用对象,生成一个新的可调用对象适配(p354)
- 定义在命名空间placeholders中的_n来指定绑定参数位置(p355)
- ref()来指定引用对象(p357)
- iterator中还定义了几个额外的迭代器:插入迭代器、流迭代器、反向迭代器和移动迭代器(p357)
- back_inserter(容器):插入到尾;front_inserter(容器):插入到头;inserter(容器,位置):插入到指定位置(p358)
- istream_iterator绑定流,在使用时才从流里读取数据(p360)
- 定义了输入、输出运算符的类型,可以使用流迭代器定义(p362)
- 反向迭代器目的是表示元素范围(p365)
- 根据算法所要求的迭代器操作,可将迭代器分为五类:输入迭代器、输出迭代器、前向迭代器、双向迭代器、随机访问迭代器(p365)
- 输入迭代器是只读不写、单遍扫描,只能递增;输入迭代器是只写不读、单遍扫描,只能递增;前向迭代器是可读写、多遍扫描,可递增;双向迭代器是可读写、多遍扫描,可递增递减;随机访问迭代器是可读写、多遍扫描,支持全部迭代器运算(p365)
- 容器命名规范_if,_copy(p368)
- list和forward_list应优先使用成员函数算法(p369)
- 链表版本的操作许多会改变底层容器(p370)
-
第11章-关联容器
- 关联容器主要有两个map和set(374)
- 标准库提供了8个关联容器,按map还是set,有序还是无序,关键字是否可重复区分(p374)
- 对map进行下标操作,若不存在则创建(p375)
- 关联容器的迭代器都是双向的(p376)
- multimap和multiset关键字可以重复(p377)
- 对于有序容器必须定义关键字的比较方法,且关键字的类型是严格弱序的(p378)
- 可以自定义关键字的比较函数,尖括号内的只是类型,需要在后面参数指出函数名,例如:set<int,bool (*)(const int&,const int&)> iset(compare);其中compare是一个前面指定的类型的函数,lambda表达式也行(p379)
- pair定义在utility中,包含两个public的数据成员first和second,pair默认构造函数会默认初始化元素,可以直接给定值,也可以从初始化列表初始化pair(p380)
- key_type:关键字类型,value_type:元素类型,map中元素类型为pair,set中元素类型为关键字的类型,mapped_type,map值类型(p381)
- 关联容器迭代器,获取的map的关键字是只读的,set的关键字也是只读的(p382)
- 通常不对关联容器使用泛型算法,因为关键字是const的(p383)
- 非重复关联容器insert返回一个pair类型,first是插入元素的引用,second是插入是否成功(p384)
- 可重复的关联容器insert操作只返回插入的元素的值(p386)
- erase删除关键字,返回删除元素的个数,根据迭代器删除,和顺序容器返回类似(p386)
- map和unordered_map支持下标操作,和at,当不存在时,下标操作创建,at抛异常,set不支持下标操作(p388)
- find、count、lower_bound、upper_bound、equal_range(p389)
- 当给定关键字不存在时,upper_bound、lower_bound、equal_bound都返回适合插入的位置(p391)
- 无序容器的存储组织为一组桶,无序容器的性能依赖于哈希函数的质量和桶的数量及大小(p395)
-
第12章-动态内存
- c++标准库提供了两种智能指针来管理动态对象,shared_ptr和unique_ptr(p400)
- 最安全的分配和使用动态内存的方法是调用一个名为make_shared的函数(p401)
- shared_ptr使用引用计数来实现其功能,当引用计数为0时,自动销毁其所管理对象(p402)
- 程序使用动态内存的三种原因:程序不知道自己需要多少对象;程序不知道所需对象的准确类型;程序需要在多个对象间共享数据(p404)
- 默认情况下,动态内存的对象默认初始化,内置类型或组合类型的对象值将未定义,类类型对象采用默认构造函数初始化(p407)
- 如果我们提供了一个括号包围的初始化器,可以使用auto来初始化,形如:auto name = new auto(type);(p408)
- 使用new分配const对象是合法的(p408)
- 通过传给new额外的参数 nothrow 阻止new抛出异常(p408)
- 编译器无法区分一个指针指向的是静态还是动态分配的对象(p409)
- delete后的指针可以通过指向nullptr来防止出现一些未定义操作(p411)
- 接受指针初始化的shared_ptr的构造函数是explicit的不可隐式转化(p412)
- shared_ptr提供了get函数返回内置指针,不要试图释放这个指针的内存,否则在shared_ptr自动销毁时会引发错误(p414)
- reset函数销毁指针,shared_ptr为空(p414)
- 使用智能指针可以防止程序在中途出现异常而导致的内存无法释放的行为(p415)
- shared_ptr可以传入一个删除器来指定当指针销毁时的操作(p416)
- 智能指针使用规范:不要使用相同的内置类型指针初始化多个智能指针;不要delete get函数返回的指针;不要使用get初始化或reset另一个智能指针;使用get获取的指针,在智能指针销毁后就无效了;若智能指针管理的不是new分配的内存,要给它传递一个分配器(p417)
- 某个时刻只能有一个unique_ptr指向一个给定的对象(p417)
- unique_ptr不能拷贝和赋值,但例如传递参数和返回参数时,即unique_ptr将要被销毁时,例外(p418)
- auto_ptr有unique_ptr部分特性,虽然其仍是标准库的一部分,不建议使用(p419)
- unique_ptr的删除器需要在构建模板时指定类型(p419)
- weak_ptr指向一个由shared_ptr管理的对象,但不会增加引用计数(p420)
- weak_ptr使用lock检查关联对象的引用计数,若计数为0,返回空的shared_ptr,否则返回一个指向该对象的shared_ptr(p420)
- 动态数组并不是数组类型,不能使用begin和end函数(p424)
- 可以在动态数组后加一个空括号对数组中元素初始化,也可以使用花括号进行指定元素的初始化(p424)
- 动态分配空数组是合法的,定义一个长度为零的数组是不合法的(p425)
- unique_ptr可以指向数组,并且无法使用成员访问控制符,可直接使用下标(p426)
- shared_ptr不直接支持动态数组,若使用必须为其指定删除器(p426)
- 使用allocator构造对象的生命周期1.alloc分配内存2.construct初始化3.destory析构4.deallocate释放内存(p427)
- allocator的伴随算法uninitialized可以在未初始化内存中创建对象(p429)
-
第13章-拷贝控制
- 若类未定义拷贝构造函数,编译器会为其自动定义一个合成的(p440)
- 构造函数的第一个参数是自身类类型引用且其他额外参数都有默认值,则此函数为该类的拷贝构造函数(p440)
- 可以定义一个非const类型引用的拷贝构造函数版本(p440)
- 拷贝构造函数通常不应是explicit的(p440)
- 直接初始化和拷贝初始化是不同的概念,直接初始化根据函数匹配原则来选择构造函数,拷贝初始化是将右侧对象拷贝到正在创建对象中,若需要则进行类型转化,不少编译器对此进行了优化,不过仍需注意(p441)
- 未定义拷贝赋值符的类,编译器会为其定义合成的(p443)
- 未定义析构函数的类,编译器会为其定义合成析构函数(p446)
- 三/五法则:1.一个类需要自定义析构函数,那么它一定需要自定义拷贝构造函数和拷贝控制符。2.需要拷贝操作的类也需要赋值操作,反之亦然。3.析构函数不能是删除的成员。4.如果一个类有已删除的或不可访问的析构函数,则其默认和拷贝构造函数是会被定义为已删除的。5.一个类有const或引用成员则不可使用合成的拷贝赋值操作。(p448,p448,p450,p450,p450)
- 只有合成的成员函数才可以使用=default,在函数内定义的默认是内联的(p449)
- =delete在函数第一次声明时使用,表明这个函数是删除的,且可以对任何函数使用(p450)
- 析构函数不能是删除的成员,否则无法定义该类的变量或临时对象,只能动态的分配这种类型的对象,但无法释放(p450)
- 在C++11新标准前,将合成的成员函数声明为private来实现阻止类的某些功能,但类的友元和成员函数仍可访问,虽然只声明不定义,不会出现错误,但试图使用这些函数时会出现链接错误,因此新标准下应使用=delete(p451)
- 类的拷贝赋值符要注意释放旧的内存(p453)
- 类定义了自己的swap,算法将优先使用类自身的swap函数(p457)
- 使用拷贝并交换技术是异常安全的,并可以正确处理自赋值(p459)
- 使用std::move来替代拷贝构造函数,避免在分配和释放内存时的消耗(p469)
- C++11新标准中,可以使用容器保存不可拷贝的类型,只要可以移动(p470)
- 右值引用绑定到右值,通过&&来获取右值,右值引用只能绑定到一个要销毁的值(p471)
- 不能将一个右值引用绑定到左值,可以使用std::move函数将左值变为右值(p472)
- noexcept可以标记函数不能抛出异常(p474)
- 移后源对象必须可以析构(一般仅用于析构),用户不能对其中的值做出任何假设(p475)
- 自定的移动构造、拷贝构造函数规则(p476)
- 如果没有移动构造函数,可以使用拷贝构造函数,传入右值参数,且是正确的(p477)
- 移动迭代器解引用返回一个右值(p480)
- 不要随意使用移动和右值(p481)
- 可以根据引用限定符来重载成员函数 void fun()&&和void fun() const &,来相应分别是右值和左值的调用(p484)
- 如果一个成员函数有引用限定符,则其相同参数列表的所有版本必须都有引用限定符(p485)
-
第14章-重载运算与类型转换
- 除了operator()外,其他重载运算符不能有默认参数(p490)
- 当一个重载的运算符是成员函数时,这时this指针默认绑定到左侧运算对象,因此成员运算符的参数数量比运算对象的数量少一个(p490)
- 不能重定义内置的运算(p490)
- 重载的运算符,其优先级和结合律,与内置运算符一致(p490)
- 通常不应重载逗号、取地址、逻辑与和或运算符(p492)
- 输入输出运算符通常是非成员函数(p494)
- 输入运算符需要处理可能失败的情况,输出不需要(p495)
- 读取错误时,输入运算符还应负责错误恢复(p496)
- 定义运算符应符合运算符的普遍规范(p498)
- 赋值“=”和复合赋值“+=”运算符应定义为成员函数,且应返回左侧对象的引用(p500)
- 下标运算符必须为成员函数,且一般有两个版本,返回普通引用和返回常量引用(p501)
- 定义递增和递减运算符应同时其前置和后置版本,前置版本返回引用,后置版本形参用一个int类型区分,返回值(p503)
- 函数调用运算符必须是成员函数。一个类可以定义多个不同版本的调用运算符,相互之间应在参数和类型上有区别,我们称该类的对象为函数对象(p506)
- 函数对象通常含有其他的成员变量,因此是有状态的,其通常作为泛型算法的实参(p507)
- lambda表达式是一个未命名类的未命名对象(p508)
- 标准库中定义了一系列的算数、关系、逻辑运算符的类(p509)
- 标准库的function定义在functional头文件中,他是一个模板,用来存储可调用对象(p512)
- 当存入对象出现二义性时,一般是函数重载,应当存入函数指针(p513)
- 类型转换运算符 operator type() const; 类型转换运算符可以面向任何类型(除了void),且该类型可以作为返回值(p514)
- 避免过度使用类型转换函数(p515)
- explicit 作用于类型转换运算符前,防止隐式转换,尤其是转化为bool时(p516)
- 避免出现二义性的转换:不要令两个类执行相同的类型转换;避免转化目标是内置类型,若已经定义,则不要重载算数运算符,不要定义转化到多种算术类型的类型转换(p519)
- 若重载时使用显式转换,则说明程序设计不足(p520)
- 如果对同一类,即提供了转换目标时算数类型的类型转换,又提供了重载,则将会遇到重载运算符与内置运算符的二义性问题(p522)
-
第15章-面向对象程序设计
- 面向对象程序设计的核心思想是数据抽象、继承和动态绑定(p526)
- C++11允许派生类显示标注它的哪个成员函数是改写的基类的虚函数,在函数参数列表后加上一个override关键字(p527)
- 使用基类的引用或指针调用虚函数时发生动态绑定(p527)
- 基类通常需要定义一个虚析构函数,即使没有任何实际操作也是(p528)
- 任何构造函数外的非静态成员函数都可以是虚函数,virtual关键字只出现在类内的声明前,不出现在类外的定义(p528)
- 每个类控制自己的成员初始化过程(p531)
- 派生类首先初始化基类的部分,再按照声明初始化派生类成员(p531)
- 在类的名字后加上final关键字可以防止类被继承(p533)
- 智能指针也支持派生类向基类的类型转换(p534)
- 不存在基类向派生类的隐式类型转换(p534)
- 派生类所覆盖的基类的虚函数,参数和返回类型必须一致,例外为:当基类虚函数返回一个基类的指针或引用时,派生类可以返回对应的派生类类型的指针或引用(返回参数只要协变都是可以的)(p537)
- 基类中的虚函数在派生类中也是虚函数,派生类覆盖虚函数时,函数在基类的形参必须与派生类的形参严格匹配(p538)
- 虚函数拥有默认实参,基类和派生类可以有不同默认参数,函数调用时的默认实参由对象的实际类型决定,基类和派生类的默认参数最后一致最好(p539)
- 通常情况下成员函数或友元函数使用域作用符来回避虚函数机制(p539)
- 纯虚函数在成员函数后加一个=0即可,其只出现在成员函数声明处(p541)
- 含有纯虚函数的类被称为抽象基类,抽象基类不能直接创建(p541)
- 不能继承友元关系,每个类负责自己的各成员的访问权限(p545)
- 使用using声明可以改变个别成员的可访问性,可以改变基类的任何可访问成员的访问性(p546)
- struct默认派生类是公有的,class默认是私有的(p546)
- 派生类的作用域位于基类的作用域之内(p547)
- 编译时的名字查找,一个对象、指针、引用的静态类型决定了,该对象的哪些成员是可见的(p547)
- 派生类隐藏基类的同名成员(p548)
- 成员函数的调用:1.确定成员的静态类型。2.在静态类型中查找函数,若找不到就到基类中找,直到继承顶端,还找不到就报错。3.若找到则进行类型检查,确定此次调用是否合法。4.若调用合法,则根据是否虚函数产生不同代码(p549)
- 成员函数无论虚函数都能被重载(p551)
- 在基类中将析构函数定义为虚函数,可以保证执行正确的析构函数(p552)
- 派生类中合成的操作也会对基类进行相应的操作(p552)
- 基类中的合成的操作被定义为已删除的,则派生类中的相应合成操作也是已删除的(p553)
- 派生类定义了拷贝或移动操作时,也要拷贝或移动基类的成员(p555)
- 析构或构造函数执行了某个虚函数,应当执行与构造函数或析构函数所属类型的对应虚函数版本(p556)
- C++11中派生类在构造时可以直接重用基类的构造函数,在派生类中使用using base::base;编译器会为基类的每个定义的构造函数生成一个对应的派生类中的构造函数(p557)
- 容器内存放继承体系对象,通常采用间接存储(p558)
-
第16章-模板与泛型编程
- 模板定义中,模板参数列表不能为空(p578)
- 模板参数前要加关键字typename或class,两者含义一样,typename后引入的(p580)
- 不能把普通的局部对象或者动态对象 绑定 指针或引用的非类型形参, 可以使用全局类型进行绑定,常用的有整形、指针、引用、函数指针等(p580)
- 模板中可以定义一个非类型参数,它必须是一个常量表达式(p581)
- 函数模板可以声明为inline和constexpr,必须放在模板参数列表之后(p581)
- 模板在使用时才会编译,而不是在定义时,通常模板头文件既包括声明又包括定义(p582)
- 模板编译和实例化的三个报错阶段:1.编译模板本身,通常只会发现语法错误。2.编译器遇到模板使用时,编译器会检测参数数目和类型匹配。3.模板实例化时,发现类型相关的错误,可能在链接时报错(p582)
- 保证传递给模板的参数能够正确工作,是调用者的职责(p583)
- 类模板不是类型名,实例化的类型包含模板参数(p585)
- 默认情况下,一个实例化了的类模板,其成员只有在被使用时才被实例化(p587)
- 在一个类模板作用域内,可以直接使用模板名而不必指定模板实参(p588)
- 类包含友元声明时,类与友元各自是否是模板相互无关(p588)
- 一个类可以将另一个模板的每个实例都声明为自己的友元,也可以限定特定的实例为友元(p589)
- C++11中可以将模板参数类型参数声明为友元(p590)
- C++11中允许为类模板定义一个类型别名(p590)
- 类模板也可以定义静态成员,基本操作和非模板类一样(p590)
- 模板参数遵循普通的作用域规则,如模板参数会隐藏作用域外的类型(p592)
- 声明的模板参数的名字不必和定义中相同(p593)
- 一个特定文件所需要的所有模板声明通常一起放置在文件开始位置(p593)
- 为了区分作用域运算符用来表示类型还是静态成员函数,当表示为类型时,需要加上typename来指明(p593)
- 我们可以为模板参数提供默认实参,就像给函数提供默认实参(p594)
- 对于类模板来说,即使所有的模板参数都有默认参数,也要在模板名后加上尖括号,函数模板不是必须的(p594)
- 一个类可以包含自身是模板的成员函数,这种成员函数被称为成员模板,成员模板不能是虚函数(p595)
- 类模板也可以定义其成员模板(p596)
- 实例化定义为 extern template declaration显示的实例化定义为template declaration 例如:extern template foo<int>;template foo<int>;(p598)
- 对于每一个实例声明在程序中某个位置必须有其显示的实例化定义(p598)
未完...