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和NODEBUG
程序可以包含用于调试的代码在开发过程中。当应用程序编写玩准备发布时,要先屏蔽掉调试代码。这种方法用到了两项预处理功能: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. 类的静态成员。
域操作符访问。
原文:https://www.cnblogs.com/cjj-ggboy/p/12450308.html