Effective Modern C++ 笔记 - 走进现代C++
说起知名的特性,C++11/14
有一大堆可以吹的东西,auto,智能指针,移动语意,lambda,并发——每个都是如此的重要,这章将覆盖这些内容。 精通这些特性是必要的,但是成为高效率的现代C++程序员也要求一系列小步骤。 从C++98
移步C++11/14
遇到的每个细节问题都会在本章得到答复。 应该在创建对象时用{}
而不是()
吗?为什么alias声明比typedef好?constexpr和const有什么不同?常量成员函数和线程安全有什么关系?这个列表越列越多。 这章将会逐个回答这些问题。
7. 小括号和大括号
在类中成员直接赋值时用大括号{}
就对了, 通用!
调用类构造函数时,()
和{}
行为是不同的
当类构造函数有std::initializer_list
时, 用{}
总是优先匹配初始化列表. 而()
则不会主动使用初始化列表.
当调用无参数的构造函数时, 由于使用()
会造成歧义, 应使用{}
1 | std::vector<int> v1(10, 20); // 生成10个元素, 值都是20 |
坑
注意下面的obj1
和obj2
, 区别和上面一样, 对std::initializer_list
有不同的优先级.
标准库里的std::make_unique
和std::make_shared
实现使用小括号
1 | template<typename T, typename... Ts> |
8. 优先用nullptr
代替NULL
9. 优先用using
代替typedef
using
可以这么用
1 | template<typename T> using MyList = std::list<T, MyAlloc<T>>; |
typedef
实现上面的效果要这样
1 | template<typename T> struct MyList { |
10. 优先用enum class
11. 优先用deleted functions
防止拷贝, 之前的套路是把未定义的拷贝构造函数和赋值函数放到private
里
1 | class Widget { |
C++11
以后应该这么做
1 | class Widget { |
除了在类中使用, deleted functions
也可以:
- 明确拒绝指定参数类型的函数
- 明确拒绝指定参数类型的特化
1
2
3
4
5
6bool isLucky(int);
bool isLucky(char) = delete;
template<typename T> void processPointer(T* ptr);
template<> void processPointer<void>(void*) = delete;
12. 用上override
声明覆盖函数
加上override
后, 方便编译器找出错误
另外提到, C++11
加入了左值专用方法和右值专用方法的定义.
1 | class Panel |
13. 优先使用const_iterator
- 优先使用
const_iterator
- 优先使用非成员版本的
begin
,end
,cbegin
,cend
,rbegin
, …
14. 如果函数不会抛出异常, 就声明noexcept
15. 尽量使用constexpr
- 用
constexpr
声明的对象在编译期就确定 - 用
constexpr
声明的函数, 当参数全部是编译期确定时, 函数结果也能在编译期得到. constexpr
函数如果能在编译期计算, 则在编译期计算, 否则就在运行期计算.constexpr
对象和函数应用范围更广, 这也意味着如果后悔了, 想去掉constexpr
声明, 可能会造成大范围的编译问题.1
2
3
4
5
6
7
8constexpr int pow(int base, int exp) noexcept;
constexpr auto numConds = 5;
std::array<int, pow(3, numConds)> results; // 编译期计算
auto base = readFromDB("base");
auto exp = readFromDB("exponent");
auto baseToExp = pow(base, exp); // 在运行期计算
16. const
成员函数应该线程安全
17. 了解特殊成员函数
C++11
加入了move构造函数和move赋值操作.
编译器只在下列情形下生成生成move构造和move操作
- 没有自定义拷贝
- 没有自定义move
- 没有自定义析构
同样,如果自定义了move, 也不再自动生成拷贝操作.
模板构造函数不会阻止生成拷贝和move
如果需要默认操作的特殊成员函数,用=default
声明.
1 | class Base { |