3.1 继承构造函数
通过using关键字可以继承父类的构造函数。也可以通过显式定义构造函数来阻止继承。
3.2 委派构造函数,减少构造函数的书写
class Info { public: Info() { InitReset(); } Info(int i) : Info() { type = i; } //通过调用无参构造函数,可以减少构造函数内容的重写 Info(char e) : Info() { name = e; } //委派构造函数和初始化列表不能同时存在,使用委派构造函数后,初始化代码需要放在函数体中 private: int type { 1 }; //初始化成员变量 char name { 'a' }; void InitReset() {} };
3.3.2 移动语意学
通过移动构造函数可以减少临时对象中的数据拷贝。
3.3.3 左值、右值和右值引用
有名字的可以取地址的是左值(&a),没有名字,不能取地址的是右值( &(b+c)是错误操作 ),所以a是左值,(b+c)是右值
T && a = ReturnRvalue(); //获得一个右值引用,只有在a的生命周期中,函数返回的右值持续存活
右值引用不能绑定左值,如 int c; int && d = c; //错误
可以通过<type_traits>中的模板类来判断值的类型,如 is_rvalue_reference<string &&>::value;
3.3.4 std::move 将一个左值强制转化为右值引用,从而可以通过右值引用使用该值
如果声明了移动构造函数、移动赋值函数、拷贝赋值函数和析构函数中的一个或多个,编译器不会再生成默认的拷贝构造函数。所以拷贝构造/赋值和移动构造/赋值函数必须同时提供或者都不提供。
3.5 列表初始化
等号加上赋值表达式,如 int a = 3 + 4;
等号加上花括号,如 int a = {3+4};
圆括号式的表达式 ,如 int a (3+4); 可以用于堆内存的new操作,如 int *i = new int(1);
花括号式的表达式,如int a {3+4}; double *d = new double{2.3f};
使用列表初始化,在出现类型收窄时会编译失败,如 char c = {1024}; //无法通过编译
3.8 字面量操作符 ret operator “” _x(arguments) {} 可以定义字面量操作符
struct Watt { unsigned int v; }; Watt operator "" _w(unsigned long long v) { return { (unsigned int)v }; }
如果字面量为整型数,那么字面量操作符函数只可以接受unsinged long long或const char*为参数
如果字面量为浮点型数,函数只可接受long double或const char*为参数
如果字面量为字符串,函数只接受const char*, size_t为参数
如果字面量为字符,函数只可以接受一个char为参数
4.2 auto 自动类型推导
使用auto声明的变量必须被初始化,从而使编译器能够从初始化表达式中推导出其类型
auto可以用于new关键字,auto z = new auto(1);
auto不能用作形参的类型,即使提供了默认参数,如 void fun(auto x = 1) {} //编译失败
auto也不能用于结构体的非静态成员变量,即使成员拥有初始值,如 struct str { auto var = 10; } //编译失败
不能声明auto数组
在实例化模板的时候不能使用auto作为模板参数,如 vecotr<auto> v = {1}; //编译失败
4.3 decltype可以用于获取对象类型,获取的类型可以用于定义对象,如 vector<int> vec; typedef decltype(vec.beging()) vectype;
decltype只接受表达式作为参数(如,decltype( Func(x)),而不接受 decltype( Func ) ),和auto一样在编译时确定类型
4.4 追踪返回类型,使用auto和decltype可以追踪返回类型
template<typename T1, typename T2> auto Sum(T1 & t1, T2 & t2) -> decltype(t1 + t2) { return t1 + t2; }
auto (*fp)() -> int; 等价于 int (*fp)();
5.1 强类型枚举
enum class Type: char { General, Light, Medium, Heavy };
6.1.2 常量表达式函数 constexpr
函数必须有返回值
函数体只有单一的return返回语句,函数体只有一条语句,且这条语句是return。可以有static_assert
在使用前必须已有定义
return 返回语句表达式中不能使用非常量表达式的函数、全局数据,且必须是一个常量表达式
常量表达式的构造函数约束:函数体必须为空;初始化列表只能由常量表达式来赋值。
6.1.3 常量表达式值
const int i = 1; 与 constexpr int j = 1; 大部分情况下相同,区别是如果 i 在全局名字空间中,编译器一定会为 i 产生数据,而对于 j ,如果不是有代码显示地使用了它的地址,编译器可以选择不为它产生数据,而仅将其当做编译器的值。
6.2.2 变长模板
template <typename… Elements> class tuple; // Elements为模板参数包
6.3.2 原子操作 atomic
atomic_llong total {0}; //原子数据类型,多线程访问时不需要加锁
std::atomic<T> t; //atomic类模板,可以任意定义出需要的原子类型
原子类型只能从其模板参数类型中进行构造,不允许原子类型进行拷贝构造、移动构造以及使用operator=等。
7.1.2 nullptr
nullptr不能转换为非指针类型,即使使用 reinterpret_cast<nullptr_t>()的方式也不可以
nullptr不适用于算术运算表达式,也不能进行取地址操作
7.2.2 =default 显式的声明函数为缺省函数,=delete 显式的声明函数为删除函数(不可使用和重载)
显示删除也可以避免编译器做一些不必要的隐式数据类型转换,此时不要和explicit共同使用,如 void Func(char c) = delete; 会导致 Func(‘c’);无法通过编译
7.3.2 lambda函数
[capture] (parameters) mutable ->return-types {statement}
lambda和仿函数类似,实际上仿函数是编译器实现lambda的一种方式
对于按值方式传递捕捉列表,传递的值在lambda函数定义的时候就决定了,而按引用方式传递的值则等于lamdba函数调用时的值。
lambda的捕捉列表仅能捕捉父作用域的自动变量,而对超出这个范围的变量是不能被捕捉的。
8.1.1 alignas 可以设定结构体的对齐方式,alignof获取结构体的对齐大小
struct alignas(32) AlignedColorVector { double r; double g; double b; double a; };
8.2.2 通用属性,使用左右双中括号包含
[[ attribute-list ]]
[[ attr1 ]] void fun [[ attr2 ]] ();
[[ attr1 ]] int array [[ attr2 ]] [10];
C++11中的预定义通用属性包括 [[ noreturn ]] 标识不会返回的函数,[[ carries_dependency ]] 标识不会将控制流返回给原调用函数的函数,如
[[ noreturn ]] void ThrowAway() {
throw "exception”;
}
8.3.2 UTF-8 采用的变长存储方式,无法通过数字式访问读取数据,更多的使用序列化时节省存储空间。定长的UTF-16或UTF-32更适合在内存环境中操作。
其他:
在C++11中std的bind1st和bind2st被bind模板所代替