C++11特性总汇
使用方法
将鼠标移至 "C++11特性总汇" 上面, 右方出现导航小图标, 点击后在导航列表右上角点击固定, 再拖至左方空闲区域
该总汇编号与书中一致, 若有不明白的地方请查看原著
<<深入理解C++11: C++11新特性解析与应用>>
预定义宏
211.预定义宏
212.
__func__
宏返回当前所在函数或结构体名字213.
#pragma once
/_Pragma(“once”)
该头文件只编译一次214.
__VA_ARGS__
变长参数宏定义#define PR(...) printf(__VA_ARGS__)
215.宽窄字符串的连接
支持long long int类型
22.
long long int n
至少有64位
不同类型运算拓宽
23.
(int)a + (long long int)b
a会被提升为long long型后再进行运算
__cplusplus
宏24.用于C/C++混合编写,
__cplusplus = 201103L
可用作检测编译器所支持的C++版本#ifdef __cplusplus extern "C" { #endif //some code #ifdef __cplusplus } #endif
assert
断言
- (1) 包含在
<cassert>
的断言assert(n > 0);
运行时若n不大于0则报错
(2) 静态断言static_assert(sizeof(a) == sizeof(b), "must have same width");
编译时报错,不可使用变量进行静态断言
noexcept
关键词
void func() noexcept(true);
若有异常,选择不抛出,而是直接终止运行并报错,括号内常量表达式true=不抛出, false=抛出
花括号初始化非静态成员
private: string name{"ABC"};
允许对非静态成员使用sizeof
private: int n; //sizeof(class::n)可编译通过
友元模板
29.可在模板使用友元
friend T
;
final
/override
关键词
private: void func() final; //派生不可重载 void oldFunc() override; //重载必须同名,同参数,同常量性,且被重载的是虚函数
默认模版类型
template<typename T1, typename T2 = int> class C1; //通过 template<typename T1 = int, typename T2> class C2; //报错,类模版默认类型需要从右往左 template<typename T1 = int, typename T2> void func(T1 a, T2 b); //通过,函数模版没有这规定
外部模版
extern template void func<int>(int);
继承构造函数
struct B: A{ using A::A; };
委派构造函数
class A{ public: A() : A(1234){} //先调用A(int) A(int n1) : A(n1, 'C'){} //先调用A(int, char) A(char c1) : A(2, c1){} //先调用A(int, char) private: A(int n2, char c2) : my_int(n2), my_char(c2){} int my_int; char my_char; }
应用于模版:
class A{ public: A(vector<int> &v) : A(v.begin(), v.end()); A(deque<short> &d) : A(d.begin(), d.end()); private: template<class T> A(A n1, T n2) : my_list(n1, n2) {} list(int) my_list; }
捕捉异常:
class A{ public: A() try : A(1234) {init();} catch(...) {error();} A(int n) {throw 0;} //构造时执行error(), 而不执行init() }
右值引用
- 331.指针成员拷贝构造
class A{ public: A() : my_ptr(new int(1234)) {} A(A ©) : my_ptr(new int(*copy.my_ptr)) {}//新申请一块内存,避免重复析构同一块内存 ~A() {delete my_ptr;} int *my_ptr; }
- 332.移动语义
class A{ public: A() : my_ptr(new int(1234)) {} A(const A& copy) : my_ptr(*copy.my_ptr) {} A(A&& move) : my_ptr(move.my_ptr) {move.my_ptr = nullptr;} ~A() {delete my_ptr;} int* my_ptr; } A GetTmp() { A tmp; return tmp; } int main(){ A one = GetTmp(); }
1.执行
GetTmp()
,创建A1并初始化后返回,
2.创建并初始化A2,即A2(A1)
, 执行的是move
而不是copy,它将A1的指针赋值给A2,并将A1的指针清掉,
3.析构A1
,由于指针清掉了,所以初始化时的内存没有被释放
4.创建并初始化one,即one(A2)
,重复第2步,第3步
5.析构one
,此时内存才真正被释放,它是A1初始化时申请的
333.有名字的是左值, 没名字的是右值, 左值
*p=1234
, 右值p=1234
334.在
<utility>
中提供了std::move
函数,它将左值强制转化为右值引用335.
swap(T a, T b)
函数使用移动语义进行交换,
移动构造函数不应该写抛出异常,
编译选项-fno-elide-constructors
关闭优化以使用移动/拷贝语义,否则变量直接替换成右值进行编译
- 336.完美转发
引用折叠规则:
typedef int T; typedef T& TR; //or typedef T&& TR TR a; //or TR& a , TR&& a
TR定义为
T&
时,a的类型都是A&
TR定义为T&&
时,TR
/TR&&
的类型为A&&
,TR&
的类型为A&
template <typename T, typename U> void PF(T&& t, U& func){ func(std::forward<T>(t)); }
forward()
与move()
功能相同,完美转发能够把引用类型准确传给调用函数
explicit
显式转换操作符
class A{} class B{ public: explicit operator A() const {return A();} } void func(A a){} void main(){ B b; A a1(b); //通过,直接初始化 A a2 = b; //错误 A a3 = static_cast<A>(b); //通过,显式转换 func(b); //错误 }
列表初始化
- 头文件
<initializer_list>
,声明一个以initialize_list<T>
模版类为参数的构造函数,就能够使自定义类使用列表初始化void func(initializer_list<int> numbers){} int main(){ func({1, 2, 3}); func({}); }
352.使用花括号初始化可以防止类型收窄
const int x = 1234; char a = x; //通过 char b = {x}; //错误 char* c = new char(1234); //通过 char* d = new char{1234}; //错误
36.POD类型
37.联合体
38.用户自定义字面量
内联名字空间
inline namespace space1{ class A{}; } namespace space2{ A a;//A in space1 class A{}; A b;//A in space2; }
模版的别名
template<typename T> using NewName = std::map<T, char*>; NewName<int> a; //等同于std::map<int, char*> a;
SFINAE规则
- 特殊场景使用特殊模板版本, 另外则是通用模板版本
struct A{ typedef int my_int; }; template <typename T> void func(typename T::my_int) {} //#1 template <typename T> void func(T) {} //#2 int main(){ func<A>(1234); //调用#1,因为存在 A::my_int func<int>(1234); //调用#2,因为不存在 int::my_int }
>>
右尖括号41.两个右尖括号
>
在模板中不再被判定为右移, 需要右移需要加圆括号()
auto
类型推导
int a = 1; auto b = a; //b的类型为int
编译时推导
1):auto
不能作函数形参类型
2):auto
不能对结构体中的肥静态成员进行推导
3):auto
不能声明数组
4):auto
不能在实例化模板时作为模板参数
decltype
类型推导
int a = 1; decltype(a) b; //b的类型为Int uding size_t = decltype(sizeof(0)); //与using/typydef合用
编译时推导
1):decltype
不能推导重载的函数
2):decltype
将亡值推导为右值引用
3):decltype
将左值推导为引用, 如三元运算符
,带圆括号的左值
,++i
,arr[0]
,*ptr
4): 以上都不是,则推导为本类型
追踪返回类型
template<typename T1, typename T@> auto Func(const T1& a, const T@& b) -> decltype(a + b){ return a + b; }
编译时推导
基于范围的
for
循环
vector<int> MyVector= {1, 2, 3, 4}; for(auto p : MyVector) { cout << p << endl; //p是解引用后的对象, 无需再*p解引用 }
条件: 迭代对象要实现
++
和==
等操作符,普通已知长度的数组(未知长度的不行), 类要有begin
函数和end
函数
强类型枚举
enum class MyEnum: char{ e1, e2, e3}; //定义一个以char为底层实现的强类型枚举 MyEnum a = e1; //错误 MyEnum b = MyEnum::e1; //通过 MyEnum c = 2; //错误 Myenum d = MyEnum::e2; if(d > 1){} //错误 if((char)d > 1){} //通过
智能指针
unique_ptr<int> up1(new int(11)); //无法被复制 unique_ptr<int> up2 = up1; //编译错误, 指针唯一 unique_ptr<int> up3 = move(up1); //up1的控制权转移给up3 up3.reset(); //显式释放 up1.reset(); //不会出错
unique_ptr
只允许唯一指针指向内存
shared_ptr
则允许共享同一块内存,它在实现上采用了引用计数,只有在计数归零时才真正释放内存
weak_ptr
不拥有控制权,其成员函数lock()
返回其指向内存的一个shared_ptr
对象,若其内存无效则返回nullptr
constexpr
常量表达式
函数:
constexpr int GetConst(){return 1;}
1): 函数体只能有单一的
return
返回语句
2): 函数必须有返回值(不能为void
)
3): 使用前必须已定义,即函数定义写在调用函数前面(放至后面则出错)
4):return
返回语句表达式中必须是一个常量表达式,且不能是运行时函数
值:
constexpr int a = 1;
它是编译时期的值,编译器可以选择不为它生成数据
自定义类: 必须对构造函数加上
constexpr
关键词struct MyType{ constrxpr MyType(int x): my_int(x){} int my_int; } constexpr MyType mt = {2};
1): 构造函数的函数体必须为空
2): 初始化列表只能由常量表达式来赋值
变长模板
template <typename T1, typename T2> class A{}; template <typename... SomeType> class B: private A<SomeType...>{}; B<int, char> xy;
typename
之后带...
来说明这是一个参数包,该包名字为SomeType
构造类型B时,会调用B的私有基类构造函数,并进行参数包展开
即实际上执行的是A<int, char> xy
;
template <typename... B> class MyClass; template <typename A, typename... B> class MyClass<A, B...>: private MyClass<B...>{ A my_a; }; template<> class MyClass<>{};
递归定义,在参数个数为0时结束,从右往左
参数包可以展开的位置:
1): 表达式
2): 初始化列表
3): 基类描述列表
4): 类成员初始化列表
5): 模板参数列表
6): 通用属性列表
7): lambda函数的捕捉列表
template <typename... A> class MyClass1: private OldClass<A...>{}; Myclass1 C1<X, Y>; //解包为 class MyClass1: private OldClass<X, Y>{}; template <typename... A> class MyClass2: private OldClass<A>...{}; Myclass2 C2<X, Y>; //解包为 class MyClass2: private OldClass<X>, private OldClass<Y>{}; template <typename... A> int GetSize(A... args){ int size = sizeof...(A); //使用sizeof...()获取参数包的大小(个数) return size; }
原子操作
- 头文件
<atomic>
,<thread>
atomic_int at1 {0}; atomic<int> at2 {0}; int Set(){ at1 = 1; at2 = 2; } int Show(){ cout << at1 << ", " << at2 << endl; //可能输出(1,0)/(0,2)/(1,2) } int main(){ thread t1(Set, 0); thread t2(Show, 0); t1.join(); t2.join(); cout << at1 << ", " << at2 << endl; //必输出 1, 2 }
由于编译器的优化,执行时可能会打乱实际的代码顺序
所以需要显式使用原子类的成员函数写入store()
和读取load()
这些函数有2个参数,第一个是值,第二个是操作类型
操作枚举值:
memory_order_relaxed
不对执行顺序作保证
memory_order_acquire
本线程中,所有后续的读操作必须在本条原子操作完成后执行
memory_order_release
本线程中,所有之前的写操作完成之后才能执行本条原子操作
memory_order_acq_rel
同时包含memory_order_acquire
和memory_order_release
memory_order_consume
本线程中,所有后续的有关本原子类型的操作,必须在本条原子操作完成后执行
memory_order_seq_cst
全部存取按照顺序执行store()可用:
memory_order_relaxed
,
memory_order_release
,
memory_order_seq_cst
load()可用:
memory_order_relaxed
,
memory_order_consume
,
memory_order_acquire
,
memory_order_seq_cst
RMW(Read Modify Write)同时读写操作可用: 全部
thread_local
线程局部变量
int thread_local tl;
quick_exit()
/at_quick_exit()
快速退出
struct A{}{ ~A(){}; } void Func(){} int main(){ A a; at_quick_exit(Func); //注册一个函数,在quick_exit时FILO执行 quick_exit(0); //A的析构函数不执行 }
nullptr
指针空值
= default
和= delete
默认函数
class A{ public: A() = default; //使用默认构造函数, 保持POD类型 A(const A&) = delete; //删除该函数, 且禁止重载该函数 };
lambda函数(局部函数)
int a = 1, b = 2; auto Func1 = [=]() -> int{ return a + b; }; int c = Func1(); //c = 3 auto Func2 = [](int i1, int i2) -> int{ return i1 + i2; } int d = Func2(a, b); // d = 3
[var]
值传递方式捕捉变量var
[=]
值传递方式捕捉所有父作用域的变量,包括this
[this]
值传递方式捕捉当前this
[&var]
引用传递方式捕捉变量var
[&]
引用传递方式捕捉所有父作用域的变量,包括this
[&this]
引用传递方式捕捉当前this
数据对齐
alignas(double) int a = 1; //a按照double类型宽度对齐 alignas(alignof(double)) int b = 1; //效果相同, alignof用于获取类型的宽度
通用属性
Unicode
原生字符串
参考资料: <<深入理解C++11: C++11新特性解析与应用>>