C++ 不太熟的知识点200309
1. 基本类型转换
基本内置类型分为:算术类型和空类型。
类型转换,类型所能表示的值的范围决定了转换的过程:
•当把一个非布尔类型的算术值赋给布尔类型(1字节)时。初始值为0则结果为false,否则结果为true。
•把一个布尔类型赋给非布尔类型时,初始值false则结果为0,初始值为true则结果为1。
•当把一个浮点数赋给整数类型时,进行了近似处理。结果值将仅保留浮点数中小数点之前的部分。
•当把一个整数赋给浮点类型时,小数部分记为0。如果该整数所占的空间超过了浮点类型的容量,京都可能有损失。
•当赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型标识数值总数取模后的余数。例如,8比特大小的unsigned char可以表示0至255区间的值,如果我们赋了一个区间外的值,则实际的结果是该值对256取模后所得的余数。因此,把-1赋给8比特大小的unsigned char所得的结果是255。(这种取余不同于c++11的%)。
•当赋给带符号类型一个超出它表示范围的值时,结果是未定义的。此时,程序可能继续工作、可能崩溃,也可能生成垃圾数据。
2. constexpr和常量表达式
常量表达式是指值不会改变并且在编译过程就能得到计算结果的表达式。常量表达式可以赋给const变量。
constexpr,c++11中允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化。
指针和引用都能定义成constexpr,它们的初始值受到严格限制。一个constexpr指针的初始值必须是nullptr或者0,或者是存储于某个固定地址中的对象。
必须明确的一点是,constexpr声明中如果定义了一个指针,限定符constexpr仅对指针有效,与指针所指的对象无关。
constexpr可以修饰函数返回值,使函数可以在编译时进行运算。如constexpr int func(...){};
在编译过程中进行计算优化了性能。由于其在编译过程中计算,可以用来赋给一些需要在变异期间确定变量的地方。如数组大小 int arr[func(...);
constexpr函数有一些特殊限制:
•它的函数体只能有一条return语句(这个语句可以写得比较复杂)。
•它只能调用其它同为constexpr修饰的函数。
•它只能采用constexpr变量。
将一个变量或函数进行constexpr修饰,则该变量或函数也具有const属性。但反过来不行。
3. 类型别名
typedef int cjj;
using cjj=int; (新标准)
4. auto
auto让编译器通过初始值来推算变量的类型。
使用auto也能在一条语句中声明多个变量。因为一条声明语句只能有一个基本数据类型,所以该语句中所有变量的初始基本数据类型必须一样。
使用引用其实是使用引用的对象,特别是当引用被用作初始值时,真正参与初始化的其实是引用对象的值。此时编译器以引用对象的类型作为auto的类型。
auto一般会忽略顶层const,同时底层const则会保留下来。如果希望推断出的auto类型是一个顶层const,需要明确指出:const auto i=1; //auto推断出int,i是const int类型。
5. decltype
类似于auto,不过decltype是从表达式推断出要定义的变量的类型。c++11引入。
int i=10; decltype(i) x=20; //从i的类型推断出int,声明初始化int型变量x。
如果decltype使用的表达式不是一个变量,则decltype返回表达式的类型。有些表达式返回引用类型。decltype(引用类型)推断出的类型是引用类型。
如果decltype表达式的内容是解引用操作,则decltype将得到引用类型。
对于decltype所用的表达式来说,如果变量名加上了一对括号,则得到的结果就是该变量的类型;如果给变量加上了一层或多层括号,编译器就会把它当成是一个表达式。
decltype( (variable) )的结果是引用。双层括号推断引用。单层括号只有在表达式是引用时才推断类型是引用。
6. 其它类型转换
(1)中的是基本类型的隐式转换。
两中类型可以相互转换时,它们是关联的。
函数调用和类拷贝构造也有可能发生隐式转换。
算术转换,把一种算术类型转换成另外一种算术类型。算术转换的规则定义了一套类型转换的层次,其中运算符的运算对象将转换成最宽的类型。
整型提升负责把小整数类型转换成较大的整数类型。
无符号类型的运算对象:如果一个运算对象是无符号类型、另外一个运算对象是带符号类型,而且无符号类型不小于带符号类型,那么带符号的运算对象转换成无符号的;如果带符号类型大于无符号类型,此时转换的结果依赖于机器,如果无符号类型的所有值都能存在该符号类型中,则无符号类型的运算对象转换成带符号类型,如果不能,那么带符号类型的运算对象转换成无符号类型。
其它隐式类型转换:数组转换成指针(decltype表达式是数组时,或者数组作为取地址符(&)、sizeof及typeid等运算符的运算对象时,数组不会转换成指针);指针的转换(常量整数值0或字面值nullptr能转换成任意指针类型;指向任意非常量的指针能转换成void*;指向任意对象的指针能转换成const void *)。
四种cast:static_cast、dynamic_cast、const_cast、reinterpret_cast。
7. goto语句
goto语句的作用是从goto语句无条件跳转到同一函数内的另一条语句。
语法形式:goto label;
其中label是用于标识一条语句的标示符。带标签语句是一种特殊的语句,在它之前有一个标示符以及一个冒号:
如:end: return;
goto语句和控制权转向的那条带标签的语句必须位于同一个函数之内。
(在函数帧之间跳转有函数setjmp、longjmp、sigsetjmp、siglongjmp。)
8. try语句块和异常处理
异常是存在于运行时的反常行为,这些行为超出了函数正常功能的范围。
•throw表达式,异常检测部分使用throw表达式来表示它遇到了无法处理的问题。我们说throw引发(raise)了异常。
•try语句块,异常处理部分使用try语句块处理异常。try语句块以关键字try开始,并以一个或多个catch子句结束。try语句块中代码抛出的异常通常会被某个catch子句处理。因为catch子句”处理“异常,所以它们也被称为异常处理代码。
•一套异常类,用于在throw表达式和相关的catch子句之间传递异常的具体信息。
throw表达式包含关键字throw和紧随其后的一个表达式,其中表达式的类型就是抛出的异常类型。throw表达式后面通常紧跟一个分号,从而构成一条表达式语句。
如:throw runtime_error("Data must refer to same ISBN");
异常类定义在stdexcept头文件中。
try{
....
if(...)
throw ...
....
}catch(what_error){
...
}catch(what_error){
...
}
可通过err.what()输出错误信息。
如果没找到任何匹配的catch子句,程序会转到名为terminate的标准库函数。该函数的行为与系统有关,一般情况下,执行该函数将导致程序非正常退出。
9. 含有可变形参的函数
C++11新标准提供了两种处理不同数量实参的方法,第一种是传递initializer_list的标准库类型。
initializer_list形参:如果实参数量未知但是全部实参的类型都相同,可以使用initializer_list类型的形参。initializer_list是一种标准库函数,用于表示某种特定类型的值的数组。
initializer_list<T>lst;
initializer_list<T>lst{a,b,c...};
lst2(lst);
lst2=lst;
lst.size();
lst.begin();
lst.end();
另一种可变数量实参的方法是:可变参数模板。
template<typename T, typename...Args>
void foo(const T&t, const Arg&... rest);
声明foo是一个可变参数函数模板,有一个名为T的类型参数,和一个名为Args的模板参数包。这个包表示零个或多个额外的类型参数。foo函数参数列表包含一个const&类型的参数,指向T的类型,还包含一个名为rest的函数参数包,此包表示零个或多个函数参数。
sizeof...运算符:当我们需要知道包中的元素数目时,使用sizeof...;如sizeof...(Args)。
10. 调试帮助:assert和NDEBUG
程序可以包含用于调试的代码在开发过程中。当应用程序编写玩准备发布时,要先屏蔽掉调试代码。这种方法用到了两项预处理功能:assert和NDEBUG。
assert是一种预处理宏。它的行为有点类似于内联函数。assert宏使用一个表达式作为它的条件:assert(expr);
首先expr求值,如果表达式为假(即0),assert输出信息并终止程序的执行。如果表达式为真(非0),assert什么也不做。
assert定义在cassert头文件中。
NDEBUG预处理变量:assert的行为依赖于一个名为NDEBUG的预处理变量的状态。如果定义了NDEBUG,则assert什么也不做。默认状态下没有定义NDEBUG,此时assert将执行运行时检查。
定义NDEBUG能避免检查各种条件所需的运行时开销,当然此时根本就不会执行运行时检查。
除了用于assert外,也可使用NDEBUG编写自己的条件调试代码。如果NDEBUG未定义,将执行#ifndef和#endif之间的代码;如果定义了NDEBUG,这些代码将被忽略掉。
#ifdef NDEBUG
....//代码,这段代码将在定义NDEBUG时被忽略掉
#endif
11. 函数匹配
函数重载会有多个函数名称相同形参各不相同的情况,在调用时需进行函数匹配。
确定候选函数和可行函数:
函数匹配的第一步是选定本次调用对应的重载函数集,集合中的函数称为候选函数,候选函数具备两个特征:一是与被调用的函数同名,二是其声明在调用点可见。
第二步考察本次调用提供的实参,然后从候选函数中选出能被这组实参调用的函数,这些新选出的函数称为可行函数。
可行函数也有两个特征:一是其形参数量与本次调用提供的实参数量相等,二是每个实参的类型与对应的形参类型相同,或能转换成形参的类型。
实参与形参匹配的含义可能是它们具有相同的类型,也可能是实参类型和形参类型满足转换规则。
寻找最佳匹配(如果有的话):从可行函数中找到最匹配的那个函数。它的基本思想是,实参类型与形参类型越接近,它们匹配得越好。
当无法找到最佳的匹配函数时,编译器会因为调用具有二义性而拒绝其请求:因为每个可行函数各自在一个实参上实现了更好的匹配,从整体上无法判断哪个更好。
为了确定最佳匹配,编译器将实参类型到形参类型的转换划分成几个等级,具体排序如下所示:
1. 精确匹配,包括
•实参类型和形参类型相同。
•实参从数组类型或函数类型转换成对应的指针类型。
•向实参添加顶层const或者从实参中删除顶层const。
2. 通过const转换实现的匹配。
3. 通过类型提升实现的匹配。
4. 通过算术类型转换。
5. 通过类类型转换实现的匹配。(类隐式类类型转换)
12. 函数指针
13. 名字查找
一般类的查找的顺序是从所属的最小作用域中查找,没找到时扩大查找作用域范围。最后没找到则程序报错。
类中成员查找类似,从最近的作用域查找,或从本类中查找,没找到就依次在父类中查找。(父类如果和子类有相同成员变量,这时不算重定义(普通的变量和函数重定义会出错,子函数可以重定义父类的非虚函数),但子类和父类同名成员变量不会出错或覆盖,因为变量的作用域是其所属的类的,访问父类中的同名变量可用域操作符(父类::变量))。
14. 构造函数
默认构造函数(无形参)
委托构造函数(C++11新标准,一个构造函数可使用所属类的其它构造函数进行初始化)。形式:Cjj(int x):Cjj(){...}; //这里Cjj(int )构造函数委托了Cjj()。
只接受一个实参的构造函数(或者其它参数有默认值),不用explicit修饰会有类隐式转换可能发生。
拷贝构造函数(函数返回该类的引用,且形参也是该类的const引用)
移动构造函数。
拷贝和移动构造函数一般用于拷贝初始化中,移动构造要比拷贝构造少一些拷贝操作。拷贝和移动类似于STL中的push和emplace。
15. 聚合类
类中的所有成员都是变量,不能有任何构造函数,且变量都是public权限的,且变量没有类内初始值,没有基类也没有虚函数。
16. 类的静态成员。
域操作符访问。
17. =default
使用=default将拷贝控制成员来显式地要求编译器生成合成的版本。
18. =delete
使用=delete将拷贝控制函数和拷贝赋值运算符定义为删除的函数。
析构函数不能是删除的。
19. private权限下的拷贝构造函数
预先阻止任何拷贝该类型对象的企图。试图拷贝该对象的代码会在编译期间标记为错误,成员函数或友元函数中的拷贝操作会在链接时错误。
20. 交换操作swap
swap函数参数是指针(或引用),通过指针交换指针指向的值,并且交换操作的结果在函数结束后仍有效。
21. 右值引用
引用底层使用const指针实现的,占用额外的内存空间。
常量指针必须指向已声明的对象。引用必须初始化引用一个存在的对象(临时对象不行)。int &a=1;//出错
但是指向常量的常量指针可以初始化为临时对象,即const引用。const int&a=1; //可行
&&和左值引用的区别:
1)绑定对象不同,左值引用绑定的是返回左值引用的函数、赋值、下标、解引用、前置递增递减。
2)左值持久,右值短暂,右值只能绑定到临时对象,所引用的对象将要销毁或该对象没有其它用户。
3)使用右值引用的代码可以自由的接管所引用对象的内容。
右值引用和const引用很相似。int&&a=1;const int&a=1;先在数据区开辟一个值为1的无名整型量,再将引用a与这个整型量进行绑定。但是右值引用支持rebind。(const引用等同于指向常量的常量指针,用已存在或临时变量初始化后都不能再做修改。但是右值引用&&可以重新绑定到其它临时对象。)重绑定是指初始化后可以再重新赋值。
右值引用只能初始化临时变量,用已存在变量初始化或用另一个右值引用初始化都是错误的。
C++11引入右值引用来实现移动语义和完美转发。优化性能,通过移动语义来避免无谓拷贝的问题,通过move语义来将临时生成的左值中的资源无代价的转移到另外一个对象中去,通过完美转发来解决不能按照参数实际类型来转发的问题(同时,完美转发获得的一个好处是可以实现移动语义)。
22. move语义和完美转发forward
C++11新特性move语义。move语义和右值引用经常被放在一起。
move语义可以用move赋值替代昂贵的copy赋值,完美转发使得你可以将传来的任意参数转发给其它参数,右值引用使move语义和完美转发成为可能。
使用move语义原因:
string foo(){...} string name("cjj"); name=foo();
第三句赋值会调用string的拷贝初始化,如果使用拷贝赋值操作,会发生:首先销毁name的字符串,然后将foo()返回的临时字符串拷贝到name中,最后还要销毁foo()返回的临时字符串。
移动赋值操作符重载和拷贝赋值操作符重载相似:返回值是本类的引用,都是对=操作符进行重载,但后者参数是&,前者参数是&&。(移动构造函数也是单参数,本类的&&类型参数)。
move相比于拷贝的性能优势在于:拷贝通过左值引用进行赋值,先开辟地址空间存临时数据,再将该数据赋给一个中间临时变量,再将左值引用引用到这个中间临时变量。右值引用是,先开辟地址空间存储数据,再将右值引用直接指向该数据。
完美转发forward:
引用折叠原则:A& &折叠成A&;A&& &折叠成A&;A& &&折叠成A&;A&& &&折叠成A&&;
特殊模板参数推导原则:template<class T>void fwd(TYPE t){} //如果fwd传进来的是A类型的左值,那么T被决议为A&。如果fwd传进来的是个A类型的右值,那么T被决议为A。
将上两条原则结合起来,就可以实现完美转发。
std::forward应用于forwarding reference。例如:template<class T>void fwd(T&& t){foo(std::forward<T>(t));}
forward的实现是cast的,T的推导类型取决于传参给t的是左值还是右值。forward要做的事情就是当且仅当右值传给t时,也就是T推导为非引用类型时,forward需要将t(左值)转换成右值。
forward可以在传进来的参数可推导成右值时,返回右值引用而不是左值引用。//使用左值引用会多一步中间变量,多次调用就会造成比较大的浪费。
24. lambda
25. function
类似于函数指针。
std::function<int(int)>func; int f(int..){} func=f;
26. 赋值运算符重载返回的是类引用类型。+、==、!=重载返回的是类类型或bool类型。
27. 模板
28. tuple
tuple是一种类似pair的模板结构,但其长度声明成任意的。如:tuple<int,int>t; tuple<int,double,string>tt;这样的。
tuple对象默认值初始化,即类型默认值进行初始化;也可在声明时指出初始化值,如:tuple<int,string>ttt(1,"cjj"); 声明的初始值的数目小于等于模板参数数目;也可用列表初始化。
tuple不可下标取值,其取值使用get<pos>(obj);如:get<1>(ttt);获得“cjj"。
make_tuple(1,"cjj","xyy")创建一个tuple对象,其模板参数默认推导。如:auto tttt=make_tuple(1,2,3);不应用不匹配的类型进行赋值,tuple<int,int>er=make_tuple("cjj",1);这样是错误的。
tuple对象的比较遵从字典序比较原则。
注意:get<pos>(obj)返回的是引用,根据对象的类型可以是左值引用也可以是右值引用。
tuple_size<tupleType>::value;tupleType是一个tuple模板类型,返回该模板的参数个数。如:tuple_size<tuple<int,int>>::value值为2,即两个模板参数。
tuple_element<1,tupleType>::type;返回的是tupleType这个模板类型的第i个模板参数类型,如:tuple_element<1,tuple<string,int>>::type表示string类型。
29. bitset
bitset是一个模板,类似array,它有固定大小。
bitset重载了常用的运算符,如==、!=以及按位&、|、^、~等。
初始化:
1)bitset<n>t; //长度为n,默认每位为0.
2)bitset<n>t(u); //用unsigned long long的低n位初始化,如果unsigned long long值的长度小于n则t的高位取0.
3)bitset还可以从字符串和字符数组初始化。bitset<n>t(str,pos,len); 用字符串str的pos开始的len个字符(len可不指定,默认到字符串结尾)进行初始化,从低位到高位,余下补0;如果字符串中有非0非1的字符,会报invalid_argument错误。
bitset<n>(arr,len); //用字符数组下标0开始len个字符进行初始化,从低位到高位。多余的高位补0。字符数组值也只能为0或1。
//一般用字符串不用字符数组
操作:
bs.any() //是否存在值为1的二进制位
bs.none() //是否不存在值为1的二进制位或者说是否全部位为0
bs.size() //位长,也即是非模板参数值
bs.count() //值为1的个数
bs.test(pos) //测试pos处的二进制位是否为1
bs.set() //全部位置1
bs.set(pos) //pos位处的二进制位置1
bs.reset() //全部位置0
bs.reset(pos) //pos位置0
bs.filp() //全部位逐位取反
bs.flip(pos) //pos位取反
bs.to_ulong() //将二进制转换为unsigned long输出
bs.to_string() //将二进制转换为字符串输出
~bs //按位取反
os<<b //将二进制位输出到os流
is>>b //从流输入二进制,遇到非0非1的输入位停止输入
30. 正则表达式
正则规则:https://blog.csdn.net/fengbingchun/article/details/54835571
regex头文件。
操作:
regex //声明正则模式串,用正则形式的字符串或其它正则对象初始化。如:regex pa("^[a-z]{5,10}");声明一个长在5到10之间的小写字母构成的字符串。
regex_match //将一个字符序列与正则匹配
regex_search //查找第一个与正则匹配的子序列
regex_replace //使用给定的序列替换匹配的正则表达式
31. 随机数
在新标准出现之前,C和C++都使用一个简单的rand函数生成随机数,0到某个最大值之间。
随机数引擎类和随机数分布类。
32. IO格式化输入输出
常用:dec、hex、oct
left左边填充、right右边填充
iomanip中设置操作:
setfill(ch) //用ch填充空白
setprecision(n) //将浮点精度设置为n
setbase(n) //将整数输出为n进制
32. 多重继承和虚继承
33. 运行时类型识别
RTTI的功能由两个运算符实现:typeid(返回表达式的类型)、dynamic_cast(将基类的指针或引用转换成派生类的指针或引用)。
dynamic_cast只有在基类指针或引用所指向的确实是派生类的对象时,才能正确转向,否则会报错。相比static_cast,static_cast不会在编译时报错,但如果基类指针/引用指向的不是实际的派生类对象,它不会在编译时报错,而是在运行时出错。
typeid:表达形式是typeid(e),询问e的类型,e可以是任意表达式或类型的名字。(对数组使用得到的是数组类型而不是指针)
用法:typeid(a)==typeid(b) //用于运行时类型判断
当typeid的参数e是对指针的解引用时,*p,如果指针为空则会报bad_typeid异常。
type_info类定义在typeinfo头文件中。提供如下操作:
t1==t2 如果type_info对象t1和t2表示同一种类型,返回true,否则返回false。
t1!=t2
t.name() 返回一个C风格的字符串,表示类型名字的可打印形式。
t1.before(t2) 返回一个bool值,表示t1是否位于t2之前。
因为type_info类一般作为一个基类出现,所以它应该提供一个共有的虚析构函数。
type_info类没有默认构造函数,而且它的拷贝和移动构造函数以及赋值运算符都被定义为删除的(=delete)。所以无法定义或拷贝type_info类型的对象,也不能为type_info类型的对象赋值。创建type_info对象的唯一途径是使用typeid运算符。
type_info类的成员函数name返回一个C风格的字符串,表示对象的类型名字。typeid(base).name(); typeid(derived).name()。
34. 枚举类型
35. 类成员指针
36. union
37. 局部类