IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 《Modern Effective C++》学习笔记3 转向现代C++ -> 正文阅读

[C++知识库]《Modern Effective C++》学习笔记3 转向现代C++

条款七:在创建对象时注意区分()和{}

大括号初始化的出现是为了进行统一初始化,实现一种从概念上可以用于一切场合的初始化,从以下例子中可以窥见:

?class Widget {
? ?  ......
?private:
? ? ?int x{0};   // 可行
? ? ?int y = 0;  // 可行
? ? ?int z(0);   // 不可行
?}
??
?// 不可复制对象
?std::atomic<int> ai1{0};    // 可行
?std::atomic<int> ai2(0);    // 可行
?std::atomic<int> ai3 = 0;   // 不可行

{}的优缺点

优点:

  1. 应用语境比()宽泛;

  2. 禁止了内建型别之间进行隐式窄化型别转换,而=和()会;

?double x,y,z;
?int sum{ x + y + z };   // 不可行
  1. 使用{}不会误写函数声明;

?// 对于调用没有形参的构造函数容易解析错误
?Widget w1();    // 会被解析成返回值为Widget,无输入参数的函数声明
?Widget w2{};    // 由于函数声明不能用{}来指定形参列表,使用{}就没这个问题了
? ?              // 注意语言规定这里会调用默认构造函数,而不调用std::initializer_list<>入参的构造函数
?Widget w3({});  // 这里会调用std::initializer_list<>入参的构造函数

缺点:

  1. 调用构造函数时会优先选用带std::initializer_list<T>类别的构造函数;

?vector<int> v1(10, 20);     // 20个元素,每个元素是10
?vector<int> v2{10, 20};     // 2个元素,分别是10和20

怎么选择呢?

坚持一种就好,但要了解这些限制和特殊情况。

条款八:优先选用nullptr,而非0或NULL

0和NULL都不具备指针型别,而nullptr虽然也不具有指针型别,但可以隐式转换到所有的裸指针型别。

nullptr的优点:

  1. 实际是std::nullptr_t,不具有整形型别,不会调用到整形参数的函数版本;

  2. 对于auto类型的参数校验明确了auto类型,让你明确知道这是要校验指针类型;

  3. 函数参数中使用时会优先调用指针类型的重载版本;

    注意:要避免在整形和指针型别之间重载,即不要同时存在整形和指针形参的重载函数;

条款九:优先选用别名声明而非typedef

typedef不支持模板化,但别名声明支持,例如下面代码:

?template<typename T>
?using MyAllocList = std::list<T, MyAlloc<T>>;
??
?MyAllocList<Widget> lw;

而使用typedef就需要写成这样,使用时得加上type后缀,就像C++11中的变换std::transformation<T>::type:

?template<typename T>
?struct MyAllocList {
?    typedef std::list<T, MyAlloc<T>> type;
?};
??
?MyAllocList<Widget>::type lw;

而且当你想如下使用typedef类型来创建一个目标那中的链表的话,你还必须加上第4行这个typename 关键字

?template<typename T>
?class Widget {
?private:
?    typename MyAllocList<T>::type list;
?}

条款十:优先选用限定作用域的枚举类型,而非不限作用域的枚举类型

限定作用域的枚举类型:

?enum class Color {
?    black,
?    white,
?    red
?};
??
?Color c = Color::white;

优点:

  1. 降低命名空间污染;

  2. 不会隐式转换成其他类型,只能显式转换;

  3. 限定作用域枚举的底层型别默认为int,而不限作用域的枚举类型未知;

  4. 由于底层型别已知,限定作用域枚举总是可以进行前置声明在编译时占位,而不限作用域枚举只有指定了默认底层型别后才能这么做;

不限作用域的枚举类型:

?enum color {
?    black,
?    white,
?    red
?};

优点:

  1. 会自动隐式转换成整型型别,为索引增加意义

条款十一:优先选用删除函数,而非private未定义函数

优点:

  1. 可以把删除函数声明为public,在编译时就报错,而后者在链接时才报错;

  2. 对于所有函数都可以这样做,而后者只支持成员函数;

  3. 可以指定删除模板中的某些特性类型实现,例如:

?template <>
?void processorPointer<void>(void*) = delete;

注:一般进行删除的时复制构造函数和复制赋值运算符。

条款十二:为意在改写的函数添加override声明

C++11中为override增加了一个限制,改写的函数的引用饰词必须完全相同,见下面例子:

?class Widget {
?public:
? ? ?void doWork() &;    // 这个版本的doWork仅在*this是左值时使用
? ? ?void doWork() &&;   // 这个版本的doWork仅在*this是右值时使用
?};
? ? ?
?Widget makeWidget();    // 工厂函数,返回右值
?makeWidget().doWork();  // 调用 void doWork() && 版本
??
?Widget w;
?w.doWork();             // 调用 void doWork() & 版本

条款十三:优先选用const_iterator,而非iterator

可以使用cbegin()和cend()函数返回const_iterator(C++14)

在通用代码中,优先选用非成员函数版本的begin,end和rbegin等函数,它们能对未提供该种成员函数的容器也适用。

条款十四:只要函数不会发射异常,就为其加上noexcept声明

为什么呢?

  1. 编译器知道的越多,就能把代码优化的越好;

  2. noexcept对于移动操作、swap、内存释放函数和析构函数最有用;

  3. 即使函数自己不抛出异常,但是内部调用的函数可能会抛出异常,大部分函数都是这种异常中立函数,不具备noexcept性质;

条款十五:只要有可能使用constexpr,就使用它

  1. constexpr对象都具备const属性,都不可变,且都由编译期已知的值完成初始化。

  1. constexpr函数如果传入的是编译期已知的值,则会返回编译期已生成的结果,节省了时间。如果是未知的值,就会正常计算。

从以上两点来看,constexpr扩展了语境,而且兼容了已知的情况,推荐使用。

条款十六:保证const成员函数的线程安全性

std::atomic比mutex具有更好的性能,但是只适用于单个变量或内存区域的情形,多个时mutex才是合适的选择。

条款十七:理解特种成员函数的生成机制

什么是特种成员函数?

就是那些只有在需要时才生成的默认函数,目前有如下几种:

  1. 默认构造函数

  2. 析构函数

  3. 复制构造函数

  4. 复制赋值运算符

  5. 移动构造函数

  6. 移动赋值运算符

为什么我们要了解这些函数的生成机制?

假设我们显式定义了一个析构函数,这个操作会阻止默认移动操作的生成,但是我们代码中的移动操作并不会报错,而会执行复制操作,极大的降低了性能。

那么这机制到底是什么样呢?

  • 默认构造函数:与C++98的机制相同。仅当类中不包含用户声明的构造函数时才生成。

  • 析构函数:与C++98机制基本相同,唯一的区别在于析构函数默认为noexcept。与C++98的机制相同,仅当基类的析构函数为虚的,派生类的析构函数才是虚的。

  • 复制构造函数:运行期行为与C++98相同:按成员进行非静态数据成员的复制构造。仅当类中不包含用户声明的复制构造函数时才生成。如果该类声明了移动操作,则复制构造函数将被删除。在已经存在复制赋值运算符或析构函数的条件下,仍然生成复制构造函数已经成为了被废弃的行为。因为认为存在资源的管理,默认构造函数已经无效。

  • 复制赋值运算符::运行期行为与C++98相同:按成员进行非静态数据成员的复制赋值。仅当类中不包含用户声明的复制赋值运算符时才生成。如果该类声明了移动操作,则复制赋值运算符将被删除。在已经存在复制构造函数或析构函数的条件下,仍然生成复制赋值运算符已经成为了被废弃的行为。因为认为存在资源的管理,默认构造函数已经无效。

  • 移动构造函数和移动赋值运算符::都按成员进行非静态数据成员的移动操作。仅当类中不包含用户声明的复制操作、移动操作和析构函数时才生成。生成条件最苛刻。

另外,存在一个例外,那就是成员模板函数,成员模板函数在什么时候都不会阻止生成任何特种成员函数。例如如下代码并没有影响。

?class Widget {
?    template<typename T>
? ? ?Widget(const T& rhs);       // 模板复制构造函数
? ? ?
? ? ?template<typename T>
? ? ?Widget& operator=(const T& rhs);    // 模板复制赋值运算符
?};
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-07-16 11:05:17  更:2021-07-16 11:07:22 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年4日历 -2024/4/28 17:59:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码