以下为个人学习笔记整理。参考书籍《C++ Primer Plus》
# 十八、C++11 新功能
# 新类型
- long long
 - unsigned long long
 - char16_t
 - char32_t
 - string
 
# 新的统一初始化规则
支持大括号初始化列表。
int x = {5};  | |
double y {2.1};  | |
short lst[6] = {1,2,3,4,5,6};  | 
# 缩窄
初始化列表可以防止缩窄,即禁止将数值赋值给无法存储它的数值变量。
# std::initializer_list
如果类的构造函数参数中带有 std::initializer_list ,那么初始化列表语言就只能用于构造函数。
# 声明
auto:通过 auto 实现自动类型推断的声明,被声明对象必须要显示的初始化。decltype:将变量类型声明为表达式指定的类型。
# 返回值类型后置
支持了一种新的函数声明方式:
// 两者等效 | |
double f(double, int);  | |
auto f(double, int) -> double;  | 
# 模板别名
有些模板类型命名很长,现在支持重命名,功能和 typedef 类型,但支持模板具体化操作。
// 两者等效 | |
typedef std::vector<std::string>::iterator itType;  | |
using itType = typedef std::vector<std::string>::iterator;  | |
// 支持模板具体化 | |
template<typename T>  | |
using array_12 = std::array<T, 12>  | 
# nullptr
引入空指针概念
# 智能指针
支持三种智能指针
- unique_ptr
 - shared_ptr
 - weak_ptr
 
# 异常处理相关
支持函数显示声明可能产生的异常
void f() throw(ExceptionName);  | 
# 作用域内枚举
传统枚举的作用域为当前定义的作用域。同一个作用域出现重名枚举将会有问题,另外由于枚举的底层实现不是完全可移植,所以引入了新的枚举定义方式:
enum COLOR_TYPE {RED, GREEN}; // old  | |
enum class COLOR_TYPE {RED, GREEN}; // new  | |
enum struct COLOR_TYPE {RED, GREEN}; // new  | 
# 对类的修改
# 显示转换运算符
explicit 可以限制对象自动转换,只能手动调用才会被正确执行。
class A{  | |
A(int); // automatic  | |
explicit A(double) // assign type  | |
} | |
A a_1,a_2,a_3;  | |
a_1 = 1; // ok  | |
a_2 = 1.0; // fail  | |
a_3 = A(1.0); // ok  | 
此外还支持 explicit 应用于类型转换函数
class A{  | |
operator int() const;  | |
explicit operator double() const;  | |
} | |
A a_1,a_2,a_3;  | |
int m = a_1; // ok  | |
double n_1 = a_2; // fail  | |
double n_2 = double(a_3); // ok  | 
# 类内成员初始化
现在在类的定义中可以允许初始化类成员。
class A{  | |
int m = 10;  | |
double n = 10.0;  | |
short k = 1;  | |
} | 
# 对于模板和 STL 方面的修改
# 基于范围的 for 循环
double prices[5] = {1,2,3,4,5};  | |
for (double x: prices)  | |
cout << x << endl;  | |
double prices[5] = {1,2,3,4,5};  | |
for (auto x: prices)  | |
cout << x << endl;  | |
double prices[5] = {1,2,3,4,5};  | |
for (auto& x: prices)  | |
x = 1.0;  | 
# 新增 STL 容器
forward_list:单向链表
unordered_map:底层哈希表
unordered_multimap:底层哈希表
unordered_set:底层哈希表
unordered_multiset:底层哈希表
新增模板 array
# 新增 STL 方法
- cbegin ():和 begin 类似,返回值视作 const。
 - cend ():和 end 类似,返回值视作 const。
 
# valarray 升级
支持基于范围的迭代 valarray 对象。
# 摒弃 export
# 尖括号问题
C++ 11 之前,为避免 >> 运算符 和 > 之前混淆,所以中间需要加上空格隔开,但现在不需要了
std::vector<std::list<int> > vl; // C++98  | |
std::vector<std::list<int>> vl; // C++11  | 
# 右值引用
左值引用操作相当于给对象取别名,前提是对象必须是能够获取地址的,即:对象存储在内存而非寄存器中。
int b=a;// 此时 a 在内存中  | |
int b=a+1;// 此时 a+1 在寄存器中  | |
int& b_left = b; // 声明一个左值引用 b_left, b 和 b_left 地址一样  | |
int b_copy = b; // 声明一个同数值的 b_copy, b_copy 和 b 地址不一样  | |
int& b_left = 10; // fail  | 
由于特定情况下需要获取寄存器的值或者匿名对象的值,这时候右值引用诞生了
int && b_left = 10; //ok  | |
int && b_left = x + y; // ok  | |
double && r = std::sqrt(2.0); // ok  | 
# 移动语义和右值引用
# 什么是移动语义以及为啥要它
移动语义可以在函数传递的时候不进行值拷贝而是传递地址,这里的传递地址并非函数里面传递引用的概念。
因为传递引用相当于创建一个临时变量用于存放对象地址,虽然对象本身不需要完全拷贝一份,但是地址本身是需要拷贝一次的。
而移动语义的作用则是能够让地址拷贝本身也变得不需要,尽可能的减少拷贝和创建临时变量的消耗。
class A{  | |
private:  | |
char *pc;  | |
public:  | |
A(A&& f); // 移动语义  | |
} | |
A::A(&&f){  | |
pc = f.pc;  | |
f.pc = nullptr; // 避免对象 f 销毁后调用析构函数清楚 pc 值,所以修改 pc 的指向。  | |
} | |
A('a'); // 调用移动语义进行拷贝  | 
# 赋值
除去构造函数,赋值语句同样适用。
A& A::operator=(A &&f){  | |
if(this == &f)  | |
return *this;  | |
delete []pc;  | |
pc = f.pc;  | |
f.pc = nullptr;  | |
return *this;  | |
} | 
# 强制移动
如果某些情况下,希望能够在移动语义中使用左值作为参数(有地址的非匿名对象),可以通过 move 函数。
move 函数的返回值是一个右值,因此 two = std::move(one) 先当与把一个右值传递给 two ,这会默认调用「移动赋值语句」。
如果没有定义移动赋值语句,那么会执行「赋值运算符」。但是如果两者都没定义,那么操作将失败。
#include <utility> | |
A one{'o'};  | |
A two; | |
two = std::move(one);  | 
# 类的新功能
# 特殊成员函数
新增了两个特殊成员函数:
- 默认构造函数
 - 复制构造函数
 - 复制赋值运算符
 - 析构函数
 - 移动构造函数「new」
 - 移动赋值运算符「new」
 
# 默认的方法和禁用的方法
如果定义了移动构造函数,那么编译器不会再创建默认的构造函数,但如果希望默认构造函数被创建,可以使用 default 关键字:
class A{  | |
public:  | |
A(A&&);  | |
A()=default; // 显示声明默认构造函数  | |
} | 
此外,可以通过 delete 声明某些方法不可被调用
class A{  | |
public:  | |
A(const A&) = delete; // 禁止复制构造函数  | |
A& operator=(const A&) = delete; // 禁止复制赋值运算符  | |
} | 
# 委托构造函数
委托构造函数可以再构造一个对象的时候,把其中一部分对象的构造操作「委托」给其他函数执行
class A{  | |
int k;  | |
	string str; | |
public:  | |
A(int, string);  | |
} | |
A::A(int kk, string s):k(kk),str(s){}  | 
# 继承构造函数
C++11 提供了可以让派生类能够继承基类构造函数的机制,该方法不仅可以用于构造函数,其他非特殊成员函数都🉑。
class A{  | |
public:  | |
A();  | |
} | |
class B:public A{  | |
public:  | |
using A::A;  | |
} | 
# 管理虚方法:override 和 final
C++11 提供了一个显示覆盖虚函数定义的标识符 override , 避免由于特征匹配把一些不期望覆盖的虚函数被隐藏,导致调用错误。
class A{  | |
public:  | |
virtual void f(char) const;  | |
} | |
class B:public A{  | |
public:  | |
virtual void f(char*) const; // 将把父类的 f (char) const 隐藏,从而导致调用 f (char) 版本报错。  | |
virtual void f(char*) const override; // 加上 override 显示声明需要覆盖父类虚函数,由于两者不匹配,因此可以在编译时出错,及时发现问题。  | |
} | 
final 标识符则是限制某些特定的虚函数不能够被子类所覆盖
class A{  | |
public:  | |
virtual void f(char) const final;  | |
} | |
class B:public A{  | |
public:  | |
virtual void f(char) const override; //fail,不允许覆盖父类虚函数  | |
} | 
# Lambda 函数
Lambda 又可以理解为匿名函数。
[](int x){return x % 3 == 0;} // 功能上等效于 bool f3 (int x){return x % 3 == 0}  | |
// 如果表达式内不仅仅只有 return 一条语句,则还需要带上返回值 | |
[](int x)->double{int y = x; return y - x;}  | 
# 包装器
可以封装一个函数,并且指定其中的某几个参数,返回值则是一个包装后的函数。或是通过统一的方式调用相同类型的函数:
#include <functional> | |
double dub(double x) {return 2.0 * x;}  | |
double square(double x){return x*x;}  | |
function<double(double)> f_1 = dub;  | |
function<double(double)> f_2 = square;  | |
function<double(double)> f_3 = [](double x){return x*2.5};  | |
use_f(1.0, f_1);  | |
use_f(2.0, f_2);  | |
use_f(3.0, f_3);  | 
# 可变参数模板
- 模板参数包(parameter pack)
 - 函数参数包
 - 展开(unpack)参数包
 - 递归
 
# 模板和函数参数包
C++11 提供了一个元运算符(meta-operator)「...」来声明模板参数包,表示一个类型列表:
template<typename... Args> // Args:模板参数包  | |
void show_list(Args... args){ //args:函数参数包  | |
} | 
# 展开参数包
如何展开参数包呢?在 args 右边加上「...」。
template<typename... Args> // Args:模板参数包  | |
void show_list(Args... args){ //args:函数参数包  | |
show_list(args...); //same as show_list (1,2,3) 然而这将导致无限的递归调用  | |
} | |
show_list(1,2,3)  | 
# 在可变参数模板函数中使用递归
每次确定模板参数包内的第一项参数,递归拆解,直到确定所有的参数,从而解决无限递归的情况:
void show_list(){}  | |
template<typename T, typename... Args>  | |
void show_list(T value, Args... args){  | |
cout << value;  | |
show_list(args...);  | |
} | |
show_list(1,2,3);  | 
# C++11 新增的其他功能
# 并行编程
添加了关键字 thread_local。
支持了原子操作库和线程支持库。
- atomic:原子操作库
 - thread:线程支持库
 - mutex:线程支持库
 - condition_variable:线程支持库
 - future:线程支持库
 
# 新增库
- random:提供大量随机工具
 - chrono:提供处理时间间隔
 - tuple:支持模板 tuple(元组)
 - ratio:编译阶段有理数算术库
 - regex:正则表达式库,支持模式匹配
 
# 低级编程
- 放松了 POD(Plain Old Data)要求,让一些新的数据类型满足 POD。
 - 允许共用体的成员有构造函数和析构函数。
 - 解决了内存对齐问题。某些系统需要 double 值的内存地址为「偶数」或是「8 的倍数」,详见 alignof () 和 alignas 关键字。
 - consexpr 机制让编译器能够在编译阶段计算出结果为常量的表达式。