1. 若不抛异常则使用noexcept
好处:
-
更好展示函数不会抛出异常
-
由于展开异常调用栈对代码生成有影响,因此若不抛出异常,标注
noexecpt后,编译器能尽可能优化 -
某些操作中有用
-
move操作:例如std::vector::push_back,若对象move函数为noexcept,那么通过调用std::move_if_noexcept,将拷贝操作优化为move操作 -
内存释放
-
析构函数
-
-
可定义函数视情况
noexcept- 例如
std::swap中数组和std::pair的版本
- 例如
-
大多数函数是异常中立(可能抛也可能不抛),而非
noexcept
虽然noexcept可以带来优化,但正确性更重要,若有可能出现异常,则不能加noexcept。
2. 尽量使用constexpr
语法:constexpr <expression>。
意义:表明expression的值是一定是编译期可知(且一定不变)的。
-
该值可以放到需要编译期常量的地方(例如模板参数)
-
const不如constexpr,只能保证值不变(例如临时const变量)
constexpr函数:
-
若传参编译期可知,则结果在编译期可知(即编译时就被计算),可用于放到需要编译期常量的地方
-
若传参编译期不可知,则等同普通函数,若放到需要编译期常量的地方,则编译错误
-
函数可以是构造函数、getter和setter,若传参编译期可知,那么结果(包括类中的成员)也编译期可知
-
constexpr是对象和函数接口的一部分- 相当于宣称“我能被用在要求常量表达式的地方”
3. 让const成员函数线程安全
因为const函数的意义在于调用时,不会变更成员内部值,返回时一个相同的值。
但是有时候确实必要修改很小一部分内部值,此时可以用mutable修饰这一部分值。
但修改后,多线程下,若不保证线程安全,则行为未定义,从而打破const成员函数的语义。
因此,尽量让const函数线程安全,除非不会并发访问。
4. 特殊成员函数的生成
特殊成员函数:
-
默认构造函数
-
析构函数
-
拷贝构造函数:
T(const T&) -
拷贝赋值运算:
T& T::operator=(const T&) -
移动构造函数:
T(T&&) -
移动赋值运算:
T& T::operator=(T&&)
新增的移动成员函数,默认特点:
-
对非静态成员逐个
move -
同样影响父类部分
-
若成员不支持移动,则会拷贝
总结特殊成员函数的生成和处理规则:
-
默认构造函数:仅类没有声明构造函数时生成
-
析构函数:仅类没有声明构造函数时生成,且为
noexcept -
拷贝构造函数:
-
没有定义拷贝构造时生成
- 若声明了移动操作,它默认是
delete
- 若声明了移动操作,它默认是
-
逐个拷贝非静态成员
-
-
拷贝赋值运算:
-
没有定义拷贝赋值时生成
- 若声明了移动操作,它默认是
delete
- 若声明了移动操作,它默认是
-
逐个拷贝非静态成员
-
-
移动操作(构造+赋值运算):
-
当没定义拷贝、移动和析构操作时生成
-
逐个移动(调用
std::move)非静态成员,若不支持移动,则回退为拷贝
-
-
成员函数的模板不影响生成特殊成员函数的规则