《Effective Modern C++》
1:理解template类型推导
- 在template类型推导中,作为引用的参数被视为非引用,即忽略它们的引用性。
- 当推导通用引用参数的类型时,左值参数得到特殊处理。
- 当推导值参数的类型时,常量或volatile参数应该被当作非常量和 non-volatile。
- 在推导template类型期间,数组或函数名的参数将被视为指针,除非它们用于初始化引用。
2:理解auto类型推导
- auto类型推导和template类型推导一样,但是auto类型推导假定带括号的初始值列表相当于
std::initializer_list,template类型推导不这么干。 - 函数返回类型或lambda参数中的auto表示模板类型推导,而不是auto类型推导
3:理解decltype
- decltype几乎总是在不做任何修改的情况下生成变量或表达式的类型
- 对于除变量名以外的T类型左值表达式,decltype始终报告是T&。
- C++14支持decltype(auto),它与auto一样,从其初始化器中推导出一种类型,但它使用decltype规则执行类推导。
4:知道如何查看推导出的类型
- 通常可以通过IDE编辑器、编译器错误信息和
boost::typeindex查看推导类型 - 一些工具的结果可能都不准确或精确,因此C++类型推导规则的基础是必不可少的。
5:比起显式类型声明,更喜欢auto
- auto变量必须初始化,通常不受类型不匹配的影响,这些类型不匹配可能导致可移植性或效率问题,可以简化重构过程,通常比显式指定类型的变量需要更少的类型。
- 条款2和条款6会描述auto变量所面临的陷阱。
6:当auto推导出的类型不是想要的类型时,使用显式类型初始化的惯用语法
- 不可见代理类型可以避免auto为初始化表达式推断出的错误类型。
- 显式类型初始化语法可以强制auto推导出想要的类型。
7:当创建对象时要区分()和{}
- 花括号初始化是最为广泛使用的初始化语法,可以防止类型转换变窄,也不要受c++语法解析问题的影响。
- 构造函数的重载语法分析中,花括号初始化方法只要可能就会匹配
std::initializer_list,即使其他的构造函数提供了更好的匹配参数。 - 括号和大括号之间的选择可以产生显著差异的一个例子就是:创建一个两个参数的 std::vsctor<数值类型>。
- 在template中对象创建时,括号和大括号之间的选择是一个挑战。
8:比起0和NULL,更应该用 nullptr
- 比起0和NULL,更应该用 nullptr
- 避免对整型和指针类型重载
9:与typedef相比,更喜欢别名声明
- typedef不支持模板化,但是别名声明可以。
- 别名模板避免使用
::type后缀,并且在模板中使用typename前缀。 - C++14为所有C++11类型特点转换提供别名模板。
10:与非作用域枚举相比,首选作用域枚举
- c++98枚举风格现在被看作非作用域枚举。
- 作用域枚举的枚举数仅在枚举中可见。只能通过cast转换。
- 作用域枚举和非作用域枚举都支持基础类型规范。作用域枚举的默认基础类型是int。未作用域枚举没有默认基础类型。
- 作用域枚举总是向前申明,只有在未作用域枚举的声明指定了基础类型时,未作用域才可以向前声明。
11:与私有的未定义函数相比,优先使用deleted函数
- 与私有的未定义函数相比,优先使用deleted函数
- 任何函数都可以deleted,包括非成员函数和模板实例函数
12:把重写函数声明为override
- 把重写函数声明为override
- 成员函数修饰词可以让它区别对待左值和右值对象。
13:与iterators相比,优先使用const_iterators
- 与iterators相比,优先使用
const_iterators。 - 大多数泛型代码中,优先使用非成员函数版本的begin,end,rbegin等等,而不是相应的成员函数
14:如果函数不抛出异常则声明尉noexcept
- noexcept是函数接口的一部分,意味着调用者必须依赖它。
- noexcept函数更容易优化。
- noexcept在移动操作,交换,内存释放函数,析构函数中特别有价值。
- 大多数函数是异常中立的,而不是noexcept。
15:只要有可能就使用constexpr
- constexpr对象是常量的,并且在编译时候初始化。
- constexpr函数在给定的参数是编译器已知的情况下可以生成编译器结果。
- constexpr对象和函数比起非constexpr对象和函数,可以使用在更宽泛的上下文范围。
- constexpr是对象或函数接口的一部分。
16:使const成员成为线程安全的
- 除非确定不会在并发环境中使用,否则const成员一定要是线程安全的。
- std::atomic变量比mutex性能更好,但只能用于操作单一变量或单一内存位置。
17:理解特殊成员函数的生成
- 特殊成员函数就是编译器自动生成的:默认构造函数,析构函数,拷贝操作,移动操作。
- 移动操作函数只有在没有显式声明移动操作,拷贝函数,析构函数时才生成。
- 拷贝构造函数只有在没有显式声明拷贝构造函数时才会生成,而且如果声明了移动操作就会被删除。拷贝复制操作只有在没有显式声明拷贝赋值操作符才会生成,而且生了移动操作就会删除。如果显式声明了析构函数,则拷贝操作就会过时。
- 成员函数模板永远不会阻止特殊成员函数的生成。
18:使用std::unique_ptr管理独占资源
std::unique_ptr小巧,快速,只能移动,用于管理独占资源- 默认情况先资源析构使用delete,但可以指定自定义删除器。有状态的删除器或函数指针会增加
std::unique_ptr对象的大小。 std::unique_ptr很容易转换成std::shared_ptr。
19:使用std::shared_ptr管理共享资源
std::shared_ptr对于随意的资源的共享生命周期管理提供方便的垃圾回收处理方法。- 相对于
std::shared_ptr来说,std::shared_ptr对象通常大一倍,主要是控制块,原子引用计数操作导致。 - 默认资源的析构是通过
delete,但也支持自定义删除器。删除器的类型对std::shared_ptr的类型不起作用。 - 避免从原始指针类型的变量生产
std::shared_ptrs。
20:使用std::weak_ptr代替可能发生悬空指针的std::shared_ptr
- 使用
std::weak_ptr代替可能会发生悬空指针的std::shared_ptr。 - 潜在地使用
std::weak_ptr的情景有缓存,观察者列表,避免std::shared_ptr循环引用。
21:优先使用std::make_unique和std::make_shared,而不是直接使用new
- 相对于直接使用
new来说,make函数消除了源代码重复,提升了异常安全,而且std::make_shared和std::allocate_shared都会生成更小更快的代码。 - 不适合使用
make函数的情况有指定自定义的删除器,需要传递括号初始化器。 - 对
std::shared_ptrs来说,make函数不推荐使用的其他情况还有(1)有自定义内存管理的类(2)有内存问题的系统,非常大的对象,以及std::weak_ptrs比std::shared_ptrs的生命还要长的情况。
3万+

被折叠的 条评论
为什么被折叠?



