C++ 新标准11&14
Keywords
nullptr
nullptr
属于std::nullptr_t
,其实就是typedef decltype(nullptr) nullptr_t
,所以nullptr_t
也是一种基本类型
initializer list初始化列表
使用大括号构造对象的过程:编译器首先使用大括号里的内容在只读区域构造一个临时数组array<T, N>
(也可能是只是const T[N]
),这个过程使用拷贝初始化,不允许类型窄化。之后构造出一个initializer_list<T>
,其中只包含数组首尾指针(或者只有头指针和数组长度)。编译器会把initializer_list<T>
中的元素一个一个拿出来传递给构造函数。
使用初始化列表可以实现函数变长参数,比如max({1, 2, 3})
,其实就是实现了max(initializer_list<T>)
函数
如果大括号里为空,将采用默认的参数;如果类里没有写相应参数的构造函数,也可以逐个对成员初始化,但前提是没有虚函数(不然会因为虚函数表的存在而导致成员变量内存偏移)
1 | struct Obj{ |
explicit
在旧版里可以防止当构造函数可以只接收一个参数时发生隐式类型转换,新版由于引入了initializer_list
,所以对于接收多个参数的构造函数也可能发生隐式类型转换,添加explicit
表示可以防止自动转换。
1 | struct Obj{ |
range-for
本质是使用迭代器进行遍历,对需要遍历的容器逐个取值并隐式转换为左侧声明的变量类型。如果遍历对象是一个大括号的内容,编译器将会产生一个initializer_list
。
=default & =delete
=default
只能用于Big-Five(defualt-ctor,copy-ctor,move-ctor,dtor,operator=)默认的构造函数会隐式调用父类、非静态成员的ctor,对应dtor也是如此。默认的move-ctor只是简单地调用成员的move-ctor,如果成员没有实现move-ctor,会仍然调用copy-ctor
=delete
可以用于自定义函数(=0
只能用于虚函数)
using
同typedef,块作用域-类作用域-命名空间
对模板类里的别名取别名
1 | using Iterator = std::list<int>::iterator; |
还可以使用基类的函数,包括构造函数
Alias template
template<typename T> using Vec = vector<T, MyAlloc<T>>
,自定义默认的模板参数
noexpect
如果异常没有处理就会一直向调用者传递,如果一直没有处理,最后会调用terminal
,并abort
。noexpect
表明函数不会发出异常,move-copy和move-operator=都要标识之后才会被容器扩容时(vector
和deque
)使用
override & final
override
:防止本来是要覆写,却不小心创建新的函数
final
:对于类或结构体禁止类被继承,对于函数禁止被覆写
lambda
可以不需要参数列表,还可以直接调用:[]{ cout <<"haha"<<endl; }()
定义并调用。实际上是编译器定义类,实例化函数对象,重载operator(),其中捕获的变量都是私有数据,而且当然只能捕获前面的变量。按引用捕获自然可以修改也可以接收外部的改变,而按值捕获默认是只读的,除非加mutable
,实际上改变的也是自己的成员变量
set<Person,decltype(cmp)> myset(cmp)
里必须使用decltype
,因为模板参数需要的是类型,而传递cmp对象是因为如果不传递的话,set
会实例化一个仿函数对象,然而lambda并没有default-ctor和op=,所以需要自己提供一个实例。lambda没有default-ctor可能是因为要支持变量捕获,所以对于这样容器定义的还是更推荐使用仿函数
varidic templates
需要注意省略号的位置:typename...
,Types...
,args...
,myFunc(std::forward<T>(args)...)
可以用sizeof...(args)
获取args
的参数个数
继承构造函数
声明
1 | using BaseClass::BaseClass; |
即可拥有像基类一样的构造函数,但是多余的成员并不会被初始化!
constexpr
真正的常量
1 | constexpr int f(int i){ |
原来的const
其实是readonly
(可以通过const_cast
修改)
Library
新增容器
array
内部为原生数组,forward_list
单向链表,unordered_xxx
内部为哈希表并且是拉链法
chrono
多线程
thread
std::thread
1 | auto t = std::thread(func, args); |
std::this_thread
1 | std::thread::hardware_concurrency(); // cpu个数 |
mutex
timed
前缀的可超时,recurisive
前缀的可以重入(同一线程多次加锁)
1 | std::mutex m; // std::timed_mutex timed_m; |
使用RAll的封装防止忘记解锁
1 | std::mutex m; |
condition_variable
1 | std::condition_variable cv; |
future
1 | auto res = std::async(std::launch::async, func, args); // 单独线程执行,std::launch::deferred是在调用wait/get时才非异步执行 |
atomic
原子操作保证了对数据的访问只有未开始和已完成两种状态
声明原子变量:std::atomic<int> count
std::atomic_flag
是保证无锁的bool
变量, 只能test_and_set
和clear
Compiler Optimization
返回值优化RVO
没有返回值优化时需要经过“return到临时变量,临时变量到接收变量”,可能需要两次的拷贝,其中第二次因为是右值转移,所以是肯定移动构造。
有返回值优化之后,第一次的拷贝也被省掉了。
值得注意的是,第二种写法也需要一次移动构造?也许并不需要手动写成右值引用去接收
下面是直接返回临时变量的测试,连移动构造都省掉了