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
)非静态成员,若不支持移动,则回退为拷贝
-
-
成员函数的模板不影响生成特殊成员函数的规则