explicit 显示转换操作符
默认情况下,C++编译器会在函数调用的参数和函数声明不匹配时,想方设法地将参数转为匹配的类型,让函数调用能够通过,这中间会检查:
- 实参的类型转换运算符,如果有转换为目标类型的转换运算符就调用;
- 目标类型的构造函数,看是否有接收实参类型的构造函数,如果有就调用;
有时这很方便,但更多场景下这样的行为只会导致语意上的混乱。为了避免编译器的隐式转换,可以使用explicit修饰类型转换运算符或构造函数,这样编译器就不会尝试使用对应函数进行转换。
initializer_list 初始化列表
如何使用初始化列表
C++98中,仅允许使用initializer-list初始化数组,C++11扩展了initializer-list的概念,使得普通类型也可以使用initializer-list初始化(不要把它和类的成员初始化搞混,它们的确都叫initializer-list,要区分时,可以将类的成员初始化叫做member initializer list):
int a[]={1,3,5};//C++98通过,C++11通过
int b[]{2,4,6};//C++98失败,C++11通过
vector<int> c{1,3,5};//C++98失败,C++11通过
map<int,float> d = {{1,1.0f},{2,2.0f},{5,3.2f}};//C++98失败,C++11通过
如果要让自定义的类支持这种初始化方式,只要声明一个接收在<initializer_list>中定义的initializer_list类型的构造函数就可以了。该类型是一个Iterable类型,可以使用begin, end等标准遍历方法。
防止类型收窄
使用初始化列表还可以促使编译器检查类型收窄的情况。初始化列表是目前唯一一种检查类型收窄的方式(不过事实上现在的大多数编译器在没有使用初始化列表时也会检查类型收窄并给出警告,但使用初始化列表编译器会直接给出错误)。
POD类型
POD,也就是Plain Ordinary Data,纯数据类型。
C++11对POD的定义是:平凡的,且是标准布局的。定义上,比C++98更宽容了,C++98只规定了C风格的struct是POD,但从POD的定义上,只要类对象布局是标准的,这样的类应该都是POD类。
(但是,对POD定义得更宽容似乎并没有什么意义?C++11更多的是对于哪些情况会导致对象布局变化进行了更进一步的明确,只有不导致对象布局变化的类定义才是POD类。)
union 非受限联合体
C++11将C++98对union的一些限制移除了。
- 在C++98,union中只能包含基础类型和POD类型,并且不能包含静态方法,但在C++11中,union中可以包含任意非引用类型。
- C++11中,如果union任何一个成员拥有非平凡的构造函数,那么编译器就不会为union生成默认构造函数。
- C++11中,允许在类定义使用union声明成员变量,用这种方式声明的union不需要有类型名称,被称为匿名的非受限联合体,此时联合体内的所有成员都会自动的成为类的“变长成员”,即实际上它们共享同一处内存,应该只使用它们中的某一个。
用户定义字面量
字面量操作符
- 接收字符串:<用户类型> operator "" _<后缀字符>(const char* col, size_t size)
- 接收整数:<用户类型> operator "" _<后缀字符>(unsigned long long val)
- 接收整数,但整数越界,此时会传入'\0'结尾的char :<用户类型> operator "" _<后缀字符>(const char)
- 接收浮点:<用户类型> operator "" _<后缀字符>(long double val)
- 接收字符:<用户类型> operator "" _<后缀字符>(char val)