《C++ Primer》读书笔记
把《C++ Primer》读薄系列笔记全集。
目录第I部分:C++基础
开始学习C++变量和基本类型字符串、向量和数组表达式语句函数类第II部分:C++标准库
IO库顺序容器范型算法关联容器动态内存第III部分:类设计者的工具
拷贝控制重载运算与类型转换面向对象程序设计模版与泛型编程题解修订版课后题解见GitHub仓库:cpp-primer-workbook。
开始学习C++main的返回值:0为成功状态,非0为系统定义的错误类型输入输出:计算结果为左侧运算对象,IO操作读写缓冲与程序中的动作无关输入流istream对象:cin(标准输入);流状态有效则cin为真,遇到EOF或无效输入cin为假键盘输入EOF:Windows下ctrl+d->return,Unix下ctrl+d输出流ostream对象:cout(标准输出)、cerr(警告错误、不缓冲)、clog(一般性信息、缓冲)操纵符endl:结束当前行,并将缓冲区内容刷到设备中,调试时的打印应保证一直刷新流缓冲刷新:默认情况下,cin和cerr会刷cout缓冲,程序异常终止cout不会被刷新命名空间:避免相同名字导致的冲突,调用标准库需显式说明作用域std::文件重定向:将标准输入输出与文件关联,运行命令prog1 <infile >outfile
编译:c1 /EHsc /W4 prog1.cc
prog1
echo %ERRORLEVEL%
GNUUnixg++ -std=c++11 -Wall prog1.cc
./a.out
echo $?
变量和基本类型空类型(void):无值无操作,不能定义void类型变量字符类型:char为UTF-8编码,wchar_t是确保机器可以存储及其最大扩展字符集中的任何字符的宽字符,char16_t和char32_t对应Unicode字符集内置类型的机器实现:地址表示比特串开始位置,类型决定了数据所占的比特位数及如何解释其内容浮点数:float和double分别有7和16个有效位,通常选用double,避免float的低精度及long double的低效率未定义(undefined):引发难以追踪的运行时的错误、安全问题、移植性问题赋值超范围:对无符号类型是初始值对无符号类型表示数值范围取模后的余数,而带符号类型结果是未定义有无符号类型混用:表达式中同时包含带符号和无符号类型,带符号数会自动转为无符号数整型字面值:十进制字面值默认为带符号数,是int/long/long long 中能容纳其值且尺寸最小。八进制(0开头)和十六进制(0x开头)则是int/unsigned int/unsigned long/long long/unsigned long long中尺寸最小者浮点型字面值:小数或科学计数法形式的指数,默认类型double字符串字面值:编译器在每个字符串结尾添加空字符'\0’字符前缀与类型:u(char16_t),U(char32_t),L(wchar_t),u8(char)泛化的转义序列:\x后跟十六进制数字,或\后跟1到3位八进制数字,可像普通字符一样使用列表初始化:使用花括号初始化,当存在丢失信息风险时编译器将报错,而使用()可以执行但可能发生信息丢失初始化:定义在任何函数之外的变量被初始化为0,每个类决定各自的初始化对象方式。而定义在函数体内部的内置变量类型将不初始化,试图访问未初始化的值将引发未定义行为定义:负责创建与名字关联的实体,并申请存储空间和赋初始值声明:规定变量的类型和名字,只声明不定义可在变量名前添加extern并不显式初始化,包含显式初始化即为定义引用:为对象起的别名,定义时必须初始化,不是对象,不可定义引用的引用指针:指向某个对象,定义时无需赋值,本身就是对象,对指针使用解引用符*可访问该对象指针值状态:指向一个对象、指向对象的下一个紧邻空间位置、空指针、无效指针(访问或拷贝都会出错)空指针(nullptr):不指向任何对象,可以转换成任何其他的指针类型初始化指针:建议用已定义对象或nullptr或0初始化所有指针,把任何int型变量(即使值为0)赋值给指针是错误的void*指针:可存放任意对象的地址,但不能直接操作其所指对象引用/指针类型匹配:除两种特例(const可绑定非const,基类可绑定派生类)外,指针和引用的类型都需要与之绑定的对象严格匹配复合类型判断:从右向左阅读复杂的指针或引用的声明语句,离变量名越近的符号对变量的类型有越直接的影响多文件共享const:不管声明还是定义都要添加关键字extern初始化对const的引用:允许任何表达式作为初始值,只要该表达式结果可以转化为引用类型的临时量对象指向常量的指针:自觉不去改变所指对象的值,而该对象若不是常量对象则其值通过其他方式改变const指针:必须初始化且不能修改,书写上直接在变量名之前,表示不变的是指针本身而非指针指向的对象顶层/底层const:顶层const表示本身是常量,底层const表示绑定的对象是常量;执行拷贝操作时,拷入拷出对象必须具有相同的底层const资格,或能够强制转换常量表达式:数据类型和初始值都需要是常量类型,值不会改变并在编译过程就能得到计算结果constexpr变量:一定是常量,必须用常量表达式(字面值类型,包括算术类型、引用、指针)或constexpr函数(足够简单编译时可计算结果)初始化constexpr指针:初始值是nullptr或0,或存储于某个固定地址中的对象指针、常量与类型别名:typedef char *pstring; const pstring cstr=0;
,与const char *cstr
不等价,前者的cstr是指向char的常量指针,后者中cstr是指向常量char的指针类型说明符auto:让编译器通过初始值推算变量类型,并赋诸该值;忽略顶层const,保留底层const类型指示符decltype:让编译器通过初始值推算变量类型,但不用该初始值赋值;包含顶层const,解引用、(())、赋值产生的引用都会判为引用类型预处理器:在编译前执行的一段程序,功能有替换#include的头文件,以及头文件保护符避免重复定义实体预处理变量:#define将一个名字设置为预处理变量,#ifndef及#ifdef则判断名字是否已被定义过,无视作用域规则字符串、向量和数组using声明:每个using声明引入命名空间的一个成员;头文件中的代码一般不应使用using声明string初始化:用数字和字符初始化,则string对象内容是将给定字符连续重复给定次数得到的序列getline:从输入流中读入内容直到读入换行符,保留空白符,但换行符不存入string对象中字面值与string:字符字面值和字符串字面值可以转化为string对象,相加时加号两侧的运算对象至少有一个是string型C++版的C头文件:C++版本的C标准库头文件中,定义的名字从属于命名空间std,对应的C语言的.h文件则不然size_type:string::size_type和vector::size_type可表示各自类型的长度或下标,无符号整数实例化:根据模版创建类或函数的过程,必须指明实例化成何种类型初始化特例:拷贝初始化时只能提供一个初始值;类内初始值只能使用拷贝初始化或花括号;初始元素值的列表只能放在花括号内值初始化:通常使用圆括号;但如果使用花括号但提供的值又不能用来列表初始化时,编译器也会尝试用值初始化vector比较:当元素的值可比较时,按照字典顺序进行下标运算符:vector或string的下标运算符可用于访问已存在的元素,而不能用于添加元素迭代器成员:begin和end运算符的具体类型由对象是否是常量决定,cbegin和cend始终得到const_iterator迭代器比较:两个迭代器,指向同一容器中的元素或尾元素的下一位置,比较的是位置的前后;相减得到different_type型的有符号整数,表示两个迭代器的距离定义数组:必须指定数组类型,不能使用auto由初始值推断;类型不能是引用字符数组:可以使用字符串字面型初始化,数组中需要空间放空字符复杂数组声明:从数组名字开始按照由内向外的顺序阅读,如int *(&a)[10]
表示a是对数组引用,该数组包含10个int*数组下标:通常定义为无符号类size_t类型,但与标准哭类型不同的是,数组允许负数作为下标数组与指针:多数表达式中,使用数组类型的对象其实是使用一个指向该数组首元素的指针首指针和尾后指针:使用标准库函数begin()和end()可以得到数组的首指针和尾后指针;提供这两个指针可以将数组拷贝初始化vector对象;尾后指针不指向具体元素,不能解引用或递增指针比较:只要两个指针指向同一个数组的元素或尾元素下一个位置,就能用关系运算符比较前后;两个指针相减的结果类型是有符号类型ptrdiff_tstring转char*:s.c_str()
函数将string对象s,转化为一个C风格的字符串,但无法避免s改变后之前返回的字符串失去效用多维数组初始化:使用花括号的形式,未列出的元素执行默认值初始化范围for处理多维数组:除最内层循环外,其他所有循环的控制变量都应该是引用类型,以避免这些数组形式的元素被自动转换成指向数组首元素的指针多维数组指针:多维数组的指针是指向内层数组的指针;避免指针类型混淆可使用auto或decltype,begin()和end(),类型别名简化多维数组的指针表达式左值右值:左值用的是对象的身份,右值用的是对象的值求值顺序:未指定执行顺序的表达式,如果指向并修改同一个对象,会引发错误产生未定义;只有4种运算符规定了从左到右的求值顺序(条件与&&
、条件或||
、条件运算符?:
、逗号,
)整数相除:商向0取整;取余时m%(-n)=m%(n),-m%n=-(m%n)真值测试:比较运算中除非比较对象是bool型,否则不使用布尔字面值值初始化:无论左侧运算对象是什么类型,初始值列表都可以为空,编译器会创建值初始化的临时量赋给左侧运算对象复合赋值运算符:只求值1次,普通运算符需要2次(右边表达式和赋值)整型提升:小整型(如short、char)总会自动提升为较大的整型,一般为int;较大的char(如w_char)提升为大整型中可容纳原值的最小一类移位运算符:右侧的移动位数必须非负且小于结果的位数;移出位被舍弃,符号位视机器而定sizeof运算符:可使用作用域来获取类成员大小,可使用无效的指针获取指针指向的对象所占空间,不会把数组当指针处理,对string或vector返回固定部分的大小;sizeof的返回值是常量表达式逗号运算符:先对左侧表达式求值,然后丢弃求值结果,真正的结果是右侧表达式的值数组指针转换:大多时候会自动隐式转换;当数组被当作decltype、&、sizeof、typeid的运算对象时,转换不发生指针的转换:0或nullptr可转换成任意指针类型,非底层const的指针可以转为void*
,底层const的指针可以转为const void*
命名的强制类型转换:static_cast(无底层const)、dynamic_cast(运行时类型识别)、const_cast(有底层const)、reinterpret_cast(低层的重新解释)语句switch语句:对括号内表达式求值转换为整数类型,然后与case后的每个常量表达式比较default标签:当其他case都不满足时执行,位置自由,若为空也必须跟上一个空语句( ;)或空块({})控制流跳转:在switch和goto中,不允许跨过变量的初始化语句直接跳转到该变量作用域内的另一个位置范围for语句:语法形式是for (declaration: expression) statement
;expression表示一个可以返回迭代器的begin()和end()的序列,不允许增删容器元素改变范围for中预存的end()值异常:throw引发异常,try中跑出的异常通过会被某个catch处理,在throw与catch间通过异常类传递信息寻找异常处理代码:寻找异常处理代码的过程与函数调用相反,逐层回退直到找到适当类型的catch子句;若最终未找到则会转到terminate的标准库函数,全无try语句的程序会直接调用terminate异常安全:确保对象有效,资源无泄漏,程序处于合理状态,等等异常类:头文件exception中的exception类、stdexcept中定义了几种常见的异常类、new中的bad_alloc、type_info中的bad_cast异常类初始化:exception、bad_alloc、bad_cast只允许默认初始化,what()返回编译器决定的异常信息;标准异常类必须使用string或C风格字符串初始化,what()返回该串函数函数参数:实参是函数中形参的初始值,存在对应关系,但并没有规定实参的求值顺序局部静态对象:不同于只存在于块执行期间的自动对象,局部静态对象直到程序终止才被销毁;没有显式初始值时会执行值初始化函数声明:函数可以只能声明一次,但可以定义多次,如果一个函数永远不会被用到,可以只声明不定义const参数:实参初始化形参时会忽略顶层const,所以有无顶层const版本等价,都定义则属于重复定义数组实参:不允许拷贝数组,传递数组时实际上是传递指向首元素的指针;不同大小的数组是不同类型;若形参是引用,数组不会转换为指针传多维数组:数组第二维(以及后面所有维度)的大小都是数组类型的一部分,不能省略initializer_list:实参数量未知但类型都相同,initializer_list可作形参;提供的操作类似容器,但对象中的元素永远是常量省略符形参:出现在形参列表的最后一个位置,形式为f(…)
或f(t,…)
;大多类类型对象传递给省略符形参时无法正常拷贝返回引用:不要返回局部对象的引用或指针,调用一个返回引用的函数得到左值列表初始化返回值:函数可以返回花括号包围的值的列表;若返回为内置类型,则花括号内至多一个值,而且所占空间不应大于目标类型空间main的返回值:控制到了main的结尾仍无return语句,编译器隐式插入return 0;
;头文件cstdlib中定义了预处理变量EXIT_FAILURE和EXIT_SUCCESS表示成败返回数组指针:形如Type (*function(parameter_list))[dimension]
,函数返回类型是指向大小为dimension的数组的指针尾置返回类型:形如auto function(parameter_list) -> Type (*)[dimension];
decltype确定返回类型:把左值转换为引用;由于decltype不负责把数组或函数类型转换为对应指针,所以函数声明时要加一个星号main函数:不能调用自己,不能重载const_cast和重载:常用const_cast修改const参数属性的性质,将const和非const版本的重载函数关联重载与作用域:名字查找发生在类型检查前,所以在不同作用域无法重载函数名,且内层将隐藏外层声明的同名函数形参默认值:被赋予了默认值的形参,后面所有形参都必须有默认值;在给定的作用域中一个形参只能被赋予一次默认值;可多次声明函数给不同形参赋予默认值内联函数:一般只有当函数规模小、流程直接、无递归时,内联函数的请求才会被编译器接受constexpr函数:能用于常量表达式的函数,但返回值可以是非常量;返回和形参类型都是字面值类型,函数只执行一句return;被隐式指定为内联函数;内联定义:和其他函数不同,内联和constexpr函数可以多次定义,但每次必须完全一致,通常定义在头文件中预处理宏assert:根据提供的表达式判断是否要输出错误信息并终止程序,可定义预处理变量NDEBUG禁用assert的效果NDEBUG:可在#ifndef NDEBUG
和#endif
之间编写自己的调试代码;有5个编译器预定义的名字变量__func__/__FILE__/__LINE__/__TIME__/__DATE__
,用于输出调试信息函数匹配:先选择在调用点可用的同名候选函数,再找到参数数量相等且类型相同或可强制转换的可行函数,再寻找其中最佳匹配的函数最佳匹配:每个实参的匹配都不劣于其他可行函数,且至少有一个优于其他;若找不到最佳匹配则报二义性错误实参类型转化优先级:精确匹配>const转换>类型提升>算术类型或指针转换>类类型转换函数指针:声明指向一个函数的指针,只需要用指针替换函数名;将函数名当作值使用时会自动转换成指针重载函数的指针:指针类型必须与重载函数中的某一个精确匹配函数指针形参:函数声明中,若形参是函数类型,则它会自动转换成指向函数的指针类数据抽象:定义数据成员和函数成员,抽象数据类型依赖封装封装:分离接口(用户所能执行的操作)和实现(数据成员、实现接口的函数体、私有函数)成员函数:声明必须在类内部,但定义可在类内或外,定义在类内部的函数是隐式的inline函数this:指向类类型非常量版本的常量指针,所以不能在常量类对象上调用普通成员函数常量成员函数:参数列表后带const的函数叫常量成员函数,它可将this设置为指向常量的指针,使得常量对象可访问常量成员函数,但不可写入新值;若以引用形式返回*this,则返回类型是常量引用合成的默认构造函数:会尽可能使用类内初始值初始化数据成员,当类不存在任何构造函数时编译器创建的默认构造函数;既需要其他形式又需要合成的默认构造函数,可在默认构造函数的参数列表后面加上=default
类的动态内存:当类需要分配类对象之外的资源时,合成的版本常常会失效;需要动态内存的类应该使用vector或string对象管理存储空间,避免分配和释放内存带来的复杂性class和struct:唯一区别是默认的访问权限,class为private,struct为public友元:允许其他类或函数访问非公有成员,则可在类的开头用friend关键字将其声明为友元;友元的声明仅仅限定了权限,可在真正的函数声明之后;友元函数可以定义在类的内部,但在类的外部仍然需要提供相应的声明使得函数可见可变数据成员:添加mutable关键字的数据成员,即使在const成员函数中,依旧可以被改变类内初始值:必须使用符号或花括号的直接初始化形式重载const:根据是否是常量对象,决定调用是否是常量版本的成员函数类的声明:仅声明不定义叫做前向声明:类在声明之后、定义完成之前是不完全类型,可以定义其指针或引用,作为函数的参数或返回值;只有当类全部完成后类才算被定义,所以类的成员中不能包含自己,但可以出现指针或引用类的定义:编译所有成员的声明之后才会编译函数体类型名的定义:内层作用域可以重新定义外层作用域的名字,但若外层作用域中的某个名字表示类型,则类中不能再重新定义该名字访问类成员:使用this->或类名::可强制访问类成员,无视作用域的名字查找规则;无类名加::表示全局对象成员初始化:若成员是const、引用、无默认构造函数的类,必须通过初始值列表将其初始化初始化顺序:与类中定义的顺序一致,不受初始值列表中顺序影响默认构造函数:当对象被默认初始化或值初始化时,自动执行默认构造函数;不那么明显的一种情况是类的某些数据成员缺少默认构造函数默认初始化场景:无任何初始值定义非静态变量或数组,本身含有类成员且使用合成的默认构造函数,类类型成员没有在构造函数初始值列表中显式初始化值初始化:初始值数量小于被初始化的数组大小,不使用初始值定义局部静态变量,通过T()表达式显式请求值初始化默认构造对象:使用默认构造函数定义对象的格式为类名+对象名,无之后空的圆括号对,否则将定义为函数而非对象类类型转换:构造函数只接受一个实参,则其定义了一种从构造函数的参数类型到类类型的隐式转换规则;编译器只会自动执行一步类型转换显式构造函数:在类内添加explicit关键字的构造函数只能用于直接初始化,抑制隐式转换;编译器不会在自动转换过程中使用它,但可用于显式的强制转化聚合类:所有成员public、未定义构造函数、无类内初始值、无基类或virtual函数;使用花括号初始化,花括号内的初始值顺序与声明顺序一致字面值常量类:数据成员都是字面值类型的聚合类;或数据成员都是字面值类型、至少含有一个constexpr构造函数、类内初始值是常量表达式或拥有自己的constexpr构造函数、使用析构函数的默认定义constexpr构造函数:为保证构造函数的不包含返回语句,和constexpr函数唯一可执行语句即返回函数,其函数体一般为空静态成员:独立于任何对象之外,不包含this指针;静态成员函数不能声明为const,也不能在内部使用this指针静态数据成员:必须在类的外部定义和初始化,方式和类外部定义成员函数类似;若静态数据成员为constrexpr类型,且其只限于编译器替换值的使用场景,则可以在类内定义,否则必须在类外再重新定义静态成员的优势:类内可包含自身类型的静态数据成员,但普通成员只能是指针或引用;静态成员可以作为默认实参,非静态数据成员的值属于对象的一部分,不能作为默认实参IO库IO类继承机制:ifstream和istringstream继承自istream,ofstream和ostringstream都继承自ostream宽字符IO类:在函数和类型前加前缀w,如wcin、wistreamIO对象无拷贝赋值:IO操作的函数通常以引用方式传递和返回流;由于读写会改变状态,IO对象的引用不能是const条件状态:iostate表示流状态的类型,其包含4种constexpr值,badbit(流崩溃)、failbit(可恢复错误)、goodbit、eofbit;对应4个函数bad()、fail()、good()、eof()管理条件状态:rdstate()获取状态,clear()清除所有错误标志位,clear(flags)和setstate(flags)将状态置为flags刷新输出缓冲区:可使用操纵符endl(换行)、flush、ends(空字符);开启unitbuf每次调用flush,nounitbuf解除关联流:交互式系统通常关联输入和输出流,使所有输出在读操作前被打印;每个流同时最多关联到一个流,但多个流可以关联到同一个ostream;将其关联到空指针可彻底解开关联fstream特有:打开文件绑定流的open()、关闭绑定文件的close()、文件是否成功打开且尚未关闭的is_open()stringstream特有:将s拷贝到stringstream对象的str(s)、返回保存的string的拷贝的str()顺序容器选择容器:标准库容器性能通常优于同类数据结构;C++标准新增array数组和forward_list单向链表;很多小元素且额外空间开销大,不用list或forward_list容器的额外操作:iterator表示迭代器类型,size_type无符号整型,value_type指元素类型,reference与value_type&等价size操作:返回容器内元素数量;forward_list没有size操作,其他容器的size操作是常量时间反向容器:reverse_iterator按逆序寻址的迭代器,rbegin()是尾迭代器,crend()表示const首前迭代器,forward_list:不支持反向容器迭代器,且其迭代器不支持递减运算容器初始化:必须是相同的容器类型,且保存相同类型的元素用大小初始化:只有顺序容器的构造函数才接受大小参数,关联容器不支持array:具有固定大小,列表初始化的数目必须等于或小于array的大小;花括号列表只能初始化不能赋值assign:仅顺序容器支持,传递给assign的迭代器不能指向调用assign的容器;会导致指向容器的迭代器、引用、指针失效swap:除array外,swap不对任何元素进行拷贝、删除、插入,可保证常数时间完成;除string外,指向其他容器的迭代器、引用、指针在swap后都不会失效关系运算符:两边的运算对象必须是相同的容器类型,且保存相同类型的元素插入元素:返回新添加的第一个元素的迭代器;向vector、string、deque插入元素,会使指向的迭代器、引用、指针失效;插入迭代器表示的范围内的一段元素时,不能指向与目的位置相同的容器元素是拷贝:用一个对象来初始化或插入容器时,实际上放入的是拷贝,容器中元素与提供值的对象无任何关联访问元素:at和下标操作只支持string、vector、deque、array;访问成员函数返回的是引用删除元素:clear和pop返回void,erase返回指向最后一个被删元素之后元素的迭代器特殊的forward_list操作:添加或删除通过改变给定元素之后的元素来完成;定义了首前迭代器before_begin();insert_after()返回指向最后一个插入元素的迭代器;erase_after()返回指向被删元素之后的元素的迭代器改变容器大小:resize()可用来增大或缩小容器,缩小容器则多余元素被删除,增大则填充指定值或默认初始值对象不保存尾后迭代器:在循环中插入删除deque、string、vector中元素,应每次重新调用end()管理容器大小:capacity()返回容器保留内存的大小,空容器返回0;reverse()只能增加容器保留内存大小;减少所占空间可用shrink_to_fit()申请将空间值缩小到元素数量,但不保证一定退回内存空间重新分配内存:只有执行insert时size和capacity相等,或resize、reserve时给定大小超过capacity,vector才会分配新的内存空间,分配量取决于底层实现修改string:replace和insert所允许的参数形式args依赖于范围range和位置pos是如何指定的string::npos:string搜索失败返回的static成员,类型是const string::size_type,被初始化为-1string搜索:find()第一次出现,rfind()最后一次出现,find_first_of()参数中任何一个字符第一次出现,find_last_not_of()非参数中的字符最后一次出现;找到返回下标,没找到返回nposstring数值转换:s=to_string(val); val=stoi(s, p, b);
val可以是任何算术类型,对应stoi函数替换为stol、stod等,p表示字符串开始转换的下标,b表示转换所用基数容器适配器:容器适配器接受一个已有的容器类型,使其行为看起来像一种不同类型底层容器:stack和queue默认基于deque,priority_queue默认基于vector;不能使用不能添加删除元素的array,或不能访问尾元素的forward_list作为底层容器;stack可用除array和forward_list外的任何容器构造,queue可使用list或deque,priority_queue可基于vector或deque适配器操作:每个适配器都基于底层容器类型的操作定义了自己的特殊操作,只能使用适配器操作,不能使用底层容器操作迭代器失效:添加或删除元素可能使指向容器元素的指针、引用、迭代器失效(*point).mem
;或返回自定义了箭头运算符的某个类的对象,递归执行point.operator()->mem
,直到返回指针函数调用运算符:像函数一样使用类,类包含状态所以比普通函数更灵活,定义了函数调用运算符的类叫函数对象lambda:编译器将lambda表达式翻译成未命名类的未命名函数对象,该类中包含重载的函数调用运算符;lambda默认不改变捕获的变量,所以函数调用运算符是const成员,如果lambda被声明为可变的,则调用运算符不是const;产生的类不含默认构造函数、赋值运算符及默认析构函数标准库函数对象:一组表示算术运算符、关系运算符、逻辑运算符的类,每个类分别定义了执行命名操作的调用运算符;都是模版类,并可适用于指针判断指向对象可调用对象:函数、函数指针、lambda表达式、bind创建的对象、重载了函数调用运算符的类调用形式:指明返回类型和传递给调用的实参类型,如int(int, int)
标准库function类:模版类型是调用形式,对象是可调用对象;可将运算符与对应的可调用function对象存入函数表;与旧版本的unary_function和binary_function无关,这两个已经被更通用的bind替代重载与function:将重载存入function会产生二义性问题,解决方法有存储指针和使用lambda类型转换运算符:没有显式返回类型、没有形参的const成员函数;可以面向除void外的任意可作为函数返回值的类型定义显式类型转换运算符:开头添加explicit关键字,避免隐式转换导致的异常;但如果表达式被用作条件,仍然被隐式执行;想bool的转换一般被定义为explicit operator bool()类型转换与二义性:两个类提供相同的类型转换,无法利用本身也面临二义性的强制类型转换解决;类中定义了多个转换源或目标是算数类型的转换;调用重载函数时,若多个定义的类型转换都提供了可行匹配,它们一样好;类可转化为算术类型,并重载了运算符;运算符调用不能通过调用形式区分成员和非成员面向对象程序设计面向对象程序设计(OOP):数据抽象(分离接口和实现)、继承(相似类型建模)、动态绑定(忽略相似类型区别)虚函数:基类希望派生类定义自己版本的函数,声明的返回类型前加virtual;虚函数在派生类中隐式为虚函数,可不加virtual;每个派生类中用于覆盖的虚函数,在声明后必须定义;声明时可在形参列表后加override编译检测类派生列表:冒号后紧跟以逗号隔开的直接基类列表,每个基类前有访问说明符以控制访问动态绑定:使用基类的引用或指针调用虚函数时,函数的版本在运行时根据动态类型决定派生类构造函数:每个类控制自己成员的初始化,派生类必须使用基类的构造函数初始化基类部分,没有显示初始化的部分都会执行默认初始化静态成员:整个继承体系中只存在静态成员的唯一定义派生类声明:声明语句的目的是令程序知晓某个名字的存在及它是怎样的实体,所以派生类声明不包含派生列表;一个类作为基类前,一定要定义而非仅声明防止继承:在类名或函数形参列表后加final关键字,可防止其他类再继承它派生类转基类:引用、内置指针和智能指针都支持派生类向基类的转换;编译时可知的为静态类型,运行时动态绑定变量在内存中的动态类型基类转派生类:含有虚函数的基类可使用dynamic_cast在运行时请求转换为指定派生类,确定安全可转换的使用static_cast强制覆盖掉编译器的检查工作转换规则:只对指针和引用有效,可能因访问受限无法转换;由于大多类定义了拷贝控制成员,将派生类对象也可转为基类对象,但派生部分会被切掉,只处理基类部分覆盖:派生类中与基类中形参和返回类型相同的函数可覆盖基类函数,一个例外是返回类型是指针或引用且可发生类型转换虚函数的默认实参:实参值由本次调用的静态类型决定作用域运算符:回避虚函数的动态绑定机制,覆盖原有的查找规则,指定某个版本的虚函数纯虚函数:类内函数声明语句后加=0,定义与否皆可抽象基类:含有或未经覆盖直接继承纯虚函数的类,负责定义接口给后续类覆盖;不能直接创建抽象基类的对象重构:重新设计类的体系以便移动操作或数据,必须重新编译受保护成员:类使用protected声明那些希望与派生类共享但不想被其他公共访问使用的成员;派生类的成员或友元只能通过派生类对象来访问基类的受保护对象,不能通过基类访问权限:受基类中成员和派生列表中访问说明符共同影响;struct默认公有继承,class默认私有继承;派生访问说明符对派生类成员及友元能够直接访问基类成员不影响,只会控制派生类用户(包括派生类的派生类)对基类成员的访问权限向基类转换的可访问性:只有当可以访问基类的公有成员时,决定派生类向基类的转换才是可访问的类的用户:边写代码使用类的普通用户只能访问公有成员,负责编写类成员和友元的实现者还能访问私有(实现)部分友元关系:不能传递,不能继承;每个类负责控制各自成员的访问权限改变可访问性:派生类可为那些它可以访问的名字提供using声明,置于期望的成员访问说明符后;构造函数的using声明不改变构造函数访问级别;using指定explicit或constexpr无效隐藏:派生类作用域嵌套在基类作用域内,所以派生类中定义的成员隐藏同名的基类成员名字查找与继承:确定对象的静态类型;再在本类和基类向上递归查找同名函数;找到后进行类型检查是否调用合法;合法则看调用的是不是虚函数,是虚函数则根据动态类型确定运行版本,不是虚函数或通过对象调用的则产生常规函数调用覆盖重载函数:一条基类成员函数的using声明可以把该函数所有的重载实例添加到派生类作用域中,此时派生类只需定义其特有的函数虚析构函数:基类的析构函数如果不是虚函数,delete指向派生类的基类指针将产生未定义合成拷贝控制:对直接基类部分拷贝后,再拷贝类本身的成员;销毁本身后再销毁基类,基类部分会自动销毁;基类是否是合成版本无影响,只要对应成员可访问且不是删除的函数移动操作:基类多有虚析构函数,所以不会合成移动操作,如果确实需要应首先在基类中显式定义,此时要同时显式定义拷贝操作定义拷贝操作:基类默认构造函数初始化派生类对象的基类部分,如果想拷贝或移动基类部分,则需要在派生类的构造函数初始值列表中显式使用基类的拷贝或移动构造函数析构;自定义的析构函数只负责销毁派生类自己分配的资源;构造或析构函数调用了虚函数,则应该执行与其所属类型对应版本的虚函数继承的构造函数:类不能继承默认、拷贝和移动构造函数,如果没有直接定义则编译器会合成它们,即使继承了基类的其他构造函数;派生类数据成员将被默认初始化基类构造含默认实参:派生类将获得多个继承的构造函数,每个分别省略掉一个有默认实参的形参容器与继承:容器中应放置(智能)指针而非对象继承与组合:派生类应反映与基类的Is A关系,公有派生类的对象应该可以用在任何需要基类对象的地方;类型之间Has A则暗含成员的意思接口类:在抽象基类中声明一个接口类的友元,接口类中包含该抽象基类指针,负责隐藏整个继承体系模版与泛型编程动态类型:面向对象编程在运行时确定类型,泛型编程在编译时获知类型模版:泛型编程基础,一个创建类或函数的蓝图,适用于编译时才确定类和函数类型的情况模板定义:以template开始,后跟尖括号包围的模板参数列表,内含一个或多个由逗号分隔的模板参数实例化函数模板:调用模板时,隐式或显示的指明模板实参,实例化出一个特定版本的称为实例的函数模板参数:类型参数可看作类型说明符,跟在class或typename关键字之后;非类型参数表是一个值,通过特定类型名指定,可以是常量表达式的整型,或具有静态生存期的指针或引用泛型代码原则:尽量减少对实参类型的要求,函数参数是const引用,条件判断仅用<比较运算模板编译:由于实例化时需要掌握函数模版和类模版成员函数的定义,其定义通常也放在头文件中,与普通函数和普通类成员函数不同实例化类模板:必须提供显式模板实参,每种元素类型生成的各个实例类之间无关联,拥有各自的static成员类模版成员函数:定义在类外需用template开始,后跟类模版参数列表;实例化了的类模板,其成员只有在用到时才实例化类模板和友元:非模板友元被授权访问所有类模板实例,模板友元可根据声明访问特定实例或所有实例模板类型别名:不能用typedef引用模板,新标准可使用using定义别名,如template <typename T> using partNo=pair<T, unsigned>; parNode<string> books; partNo<Student> kids;
模板参数作用域:在模板内不能重用模板参数名,正常的名字隐藏规则依旧适用;用作用域运算符访问类的类型成员时,需在句首加typename关键字,否则会被默认为static成员默认模板实参:就标准只允许类模板有默认实参,新标允许函数模板也有默认实参;调用类模板时必须加上尖括号,即使所有参数都有默认值成员模板:无论普通类还模板类中,本身是模板的成员函数叫成员模板,不能是虚函数;在类模板外定义成员模板,必须同时为两者提供模板参数列表显式实例化:避免相同模板参数产生多个实例导致的额外开销;可用extern多次声明实例,但必须有且仅有一次定义;编译器会实例化该类的所有成员,所以显式实例化提供的类型必须能用于所有成员智能指针的删除器:shared_ptr保存删除器指针,方便运行时传递可调用对象重置;unique_ptr中包含显式实例化的删除器成员,避免间接调用删除器时的运行开销类型参数转换:通常不转换而是生成一个新的模板实例;有限可用的转换规则包括const转换和数组或函数指针转换;非模板类型参数正常转换显式模板实参:在函数之后,实参列表前用尖括号提供显式实参;只有尾部的显式模板实参,并且可从函数参数推断出来的时候,才可忽略;显式指定的实参可正常类型转换尾置返回类型:需要根据传入的参数确定返回类型时,可通过decltype推断形参作为返回类型,使用尾置返回类型格式返回标准库类型转换模板:定义在头文件type_traits中,每个模板有一个公有成员type用于转换,不能转换时则返回模板参数类型本身;如remove_reference可将T&、T&&、T转换为T函数指针:用函数模板初始化函数指针时,根据指针的类型推断模板实参;使用显式模板实参消除二义性引用折叠:间接创建引用的引用,除右值引用的右值引用折叠为右值引用,其他3种引用的引用都折叠为左值引用右值引用形参:可传给它任意类型的实参,若传引用也不改变左/右值属性;通常用于模板转发实参或模板重载;使用右值引用的函数模板通常重载绑定非const右值引用或const左值引用转发:函数参数指向模板类型参数的右值引用,可保持对应实参的const属性和左/右值属性;进一步为解决接受右值引用参数不被理解为左值的问题,使用utility头文件中定义的forward,返回显式实参的右值引用,可保持实参类型的所有细节函数模版匹配:候选函数包括所有模板实参推断生成的实例,模板和非模板的可行函数统一按类型转换排序,多个函数同样好则顺次选择唯一的非模板函数、无非模板函数时最特例化的模板函数,否则调用有歧义可变参数模板:接受可变数目参数的模板函数或模板类;class...、typename...、type-name...
指定参数包,之后跟零个或多个参数,用sizeof...(args)
获取参数个数;如template <typename… Args> void foo(const Args& … args)
中,Args是模板参数包,args是函数参数包可变参数函数模板:可变参数函数通常是递归的,非可变参数版本的声明必须在作用域内,否则无限递归包扩展:将一个包分解为多个元素,并对每个元素应用模式,通过在模式后添加省略号触发转发参数包:参数定义为右值引用 foo(Args&&… args)
,转发时应用forward模式 bar(std::forward<Args>(args)…)
函数模板特例化:必须为每个模板函数提供实参,关键字template后跟一对空的尖括号;特例化的本质是实例化,而非重载,因此不影响函数匹配特例化与作用域:所有同名模板的声明应放在同一头文件的前面,在该文件内再接着声明这些模板的特例化版本。类模板特例化:若不指定全部参数部分特例化,则得到的是模板,所以要先定义模板参数;也可以事先只特例化特定成员函数,之后调用相同类型时则会调用该特例化函数而非重新实例化该成员函数无序容器与特例化:无序容器会组合使用关键字类型对应的特例化hash版本,和关键字类型上的相等运算符转载请注明出处
关键概念
重载运算符——P120
位运算符——P135
强制转换——P144
cast-name< type>(expression) type是转换的目标类型而expression是要转换的值。
cast-name包括static_cast / dynamic_cast / const_cast / reinterpret_cast 。
在分享之前,给大家推荐个我自己在学习C/C++一个很不错的群C语言/C++编程开发496926338,群内有很多免费的学习资料可供学习参考!
注意点
当一个对象被用作运算符右值的时候,用的是对象的值;当对象被用作左值的时候,用的是对象的身份(内存中的位置)。
bool b=true;bool b2=-b; //b2是true//布尔变量b的值为真,参与运算时将会提升为整数值1,对它求负后为-1.将-1转换为布尔值为true(不等于0).转换为真后为1。123123
整数和整数相除还是整数
只有四种运算符明确规定了运算对象的求值顺序。
&&
||
? :
,
条件运算符的优先级非常低,通常需要在它两端加上括号。
C语言/C++编程开发496926338
上一篇:颜值爆表星座女
下一篇:dnf结婚戒指共几种
发表评论