《Effective Modern C++》第3章 Moving to Modern C++

一、区分圆括号 () 与大括号 {} (Item 7)

C++11 引入统一初始化(brace‑initialization),即使用 {} 来初始化对象,与传统的 () 存在细微差别:

  • 避免窄化转换(narrowing)

    int x1(3.5);   // x1 == 3(隐式截断)
    int x2{3.5};   // 编译错误,防止窄化
    
  • 列表初始化优先级高于单参数构造

    struct A { A(int); A(std::initializer_list<int>); };
    A a1(1);    // 调用 A(int)
    A a2{1};    // 调用 A(std::initializer_list<int>)
    
  • 内置数组与聚合类型

    std::vector<int> v1(5, 10); // 五个元素,每个值为 10
    std::vector<int> v2{5, 10}; // 两个元素:5, 10
    

建议

  • 对基本类型和聚合类型优先使用 {},以获得更严格的类型检查和一致的语法;
  • 对于只接受单一特定参数的构造,明确使用 () 避免调用错误的初始化列表构造函数。

二、优先使用 nullptr 而非 0NULL(Item 8)

  • 问题

    • NULL 在不同平台下定义可能为 0(void*)0,带来类型模糊;
    • 使用整型 0 传给重载函数时,编译器难以区分指针重载与整数重载。
  • 解决

    void f(int);
    void f(char*);f(0);       // 调用 f(int)
    f(nullptr); // 调用 f(char*)
    

建议

  • 在所有指针上下文中使用 nullptr,保证类型安全和重载解析明确。

三、使用别名声明(using)替代 typedef(Item 9)

  • typedef 限制

    • 语法晦涩,无法用于模板别名;
    • 不易与模板参数一起阅读。
  • using 别名

    typedef std::map<std::string, std::vector<int>> MapType;
    // 改为
    using MapType = std::map<std::string, std::vector<int>>;// 模板别名
    template<typename K, typename V>
    using MapOf = std::map<K, V>;
    

建议

  • 在新代码中一律采用 using,既清晰又可与模板别名和别名模板配合使用。

四、优先使用作用域枚举(enum class)(Item 10)

  • 传统枚举问题

    • 枚举常量位于所在命名空间,易与其他符号冲突;
    • 默认可隐式转换为整型,丢失类型安全。
  • 作用域枚举优势

    enum Color { Red, Green, Blue };        // Red 与全局冲突
    enum class Shape { Circle, Square };    // Shape::Circle,无冲突int i = Shape::Circle;                  // 错误,不能隐式转换
    
  • 指定底层类型

    enum class ErrorCode : uint8_t { OK = 0, Fail = 1 };
    

建议

  • 新枚举定义一律使用 enum class
  • 如需与整型交互,可显式 static_cast

五、用已删除函数(= delete)替代私有未定义函数(Item 11)

  • 旧习惯

    class NonCopyable {
    private:NonCopyable(const NonCopyable&);NonCopyable& operator=(const NonCopyable&);
    };
    

    仅在不定义函数时会在链接期报错,且误报位置不直观。

  • 现代做法

    class NonCopyable {
    public:NonCopyable(const NonCopyable&) = delete;NonCopyable& operator=(const NonCopyable&) = delete;
    };
    

建议

  • 对于不希望调用的函数,使用 = delete,让编译器在编译期明确报错并指出源位置。

六、重写虚函数时声明 override(Item 12)

  • 风险

    • 虚函数签名微小变动会导致意外重载而非重写,潜藏运行期错误。
  • 加上 override

    struct Base { virtual void f(int); };
    struct Derived : Base {void f(int) override;       // 正确重写void f(double) override;    // 编译错误,函数签名不匹配
    };
    

建议

  • 所有重写基类虚函数的派生类函数都显式标注 override

七、优先使用 const_iterator 而非 iterator(Item 13)

  • 背景
    在不需要修改容器元素时,应使用只读迭代器以保证不被意外改变。

  • 示例

    std::vector<int> v = {/*...*/};
    for (auto it = v.cbegin(); it != v.cend(); ++it) {// it 为 const_iterator,无法通过 *it 进行写操作
    }
    

建议

  • 在遍历容器且不打算修改元素时,始终使用 cbegin()/cend() 或手动指定 const_iterator

八、声明不会抛出异常的函数为 noexcept(Item 14)

  • 好处

    • 编译器可据此做更激进的优化;
    • 在容器扩容时,若元素移动构造标记为 noexcept,可避免回退到拷贝构造。
  • 示例

    void swap(Buffer& b1, Buffer& b2) noexcept {using std::swap;swap(b1.data, b2.data);
    }
    

建议

  • 默认将不会抛出异常的函数标注 noexcept
  • 使用 noexcept(expr) 形式当抛出与否依赖于表达式。

九、尽可能使用 constexpr(Item 15)

  • 作用

    • 在编译期间求值,提高性能;
    • 构造常量对象、用作编译期上下文。
  • 示例

    constexpr int factorial(int n) {return n <= 1 ? 1 : (n * factorial(n - 1));
    }static_assert(factorial(5) == 120, "错误");
    

建议

  • 对所有能在编译期求值的函数或构造函数加上 constexpr
  • 在 C++14 及以后,constexpr 函数可包含循环和更多语句。

十、使常量成员函数线程安全(Item 16)

  • 问题
    const 成员函数默认是线程安全的吗?不是。const 只是保证不修改成员表面状态,但底层可能修改缓存等。

  • 做法

    • 对内部缓存、延迟初始化等涉及可变状态的数据成员,使用 mutable 和适当的同步机制(如 std::mutex);
    • 或者在 const 函数中不使用可变共享状态。

示例

class Data {
public:int get() const {std::lock_guard<std::mutex> lg(m_);return cachedValue_;}
private:mutable std::mutex m_;int cachedValue_;
};

十一、理解特殊成员函数的生成规则(Item 17)

C++ 会在未显式声明时自动生成默认构造、拷贝/移动构造、拷贝/移动赋值、析构函数,规则复杂:

  • 拷贝构造函数

    • 如果显式声明了移动构造或拷贝赋值,拷贝构造会被阻塞(C++11);
  • 移动构造函数

    • 如果显式声明了拷贝构造、拷贝赋值或析构,移动构造会被阻塞;
  • 析构函数

    • 显式定义后,依然会生成,但会影响其他特殊成员函数生成。

建议

  • 对于需要自定义移动或拷贝行为的类,最好同时声明并定义所有相关特殊成员函数(Rule of Five);
  • 如无需移动,应显式 = delete 移动构造与移动赋值;
  • 利用 = default 保留自动生成版本,并在声明处表达意图。

通过对以上十一个细则的深入理解与实践,你将全面掌握现代 C++ 编程中的常见陷阱与最佳实践,为编写高性能、类型安全、可维护的代码奠定坚实基础。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/914297.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/914297.shtml
英文地址,请注明出处:http://en.pswp.cn/news/914297.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Rust基础-part1

Rust基础[part1]—安装和编译 安装 ➜ rust curl --proto https --tlsv1.2 https://sh.rustup.rs -sSf | sh安装成功 [外链图片转存中…(img-ClSHJ4Op-1752058241580)] 验证 ➜ rust rustc --version zsh: command not found: rustc因为我是用的是zsh&#xff0c;所以zsh配置…

PyQt5布局管理(QGridLayout(网格布局))

QGridLayout&#xff08;网格布局&#xff09; QGridLayout&#xff08;网格布局&#xff09;是将窗口分隔成行和列的网格来进行排列。通常可以使用函数addWidget()将被管理的控件&#xff08;Widget)添加到窗口中&#xff0c;或者使用addLayout() 函数将布局&#xff08;Layou…

Java设计模式之行为型模式(责任链模式)介绍与说明

一、核心概念与定义 责任链模式是一种行为型设计模式&#xff0c;其核心思想是将请求沿着处理对象链传递&#xff0c;直到某个对象能够处理该请求为止。通过这种方式&#xff0c;解耦了请求的发送者与接收者&#xff0c;使多个对象有机会处理同一请求。 关键特点&#xff1a; 动…

SQL server之版本的初认知

SQL server之版本的初认知 为什么要编写此篇文档呢&#xff0c;主要是因为在最近测试OGG实时同步SQL server数据库表数据的时候&#xff0c;经过多次测试&#xff0c;发现在安装了一套SQL server2017初始版本&#xff0c;未安装任何补丁的时候&#xff0c;在添加TRANDATA的时候…

【前端】jQuery动态加载CSS方法总结

在jQuery 中动态加载 CSS 文件有多种方法&#xff0c;以下是几种常用实现方式&#xff1a; 方法 1&#xff1a;创建 <link> 标签&#xff08;推荐&#xff09; // 动态加载外部 CSS 文件 function loadCSS(url) {$(<link>, {rel: stylesheet,type: text/css,href:…

Python爬虫实战:研究xlwings库相关技术

1. 引言 在金融科技快速发展的背景下,数据驱动决策已成为投资领域的核心竞争力。金融市场数据具有海量、多源、实时性强等特点,传统人工收集与分析方式难以满足高效决策需求。Python 凭借其丰富的开源库生态,成为金融数据分析的首选语言。结合 Requests、BeautifulSoup 等爬…

Linux 内核日志中常见错误

目录 **1. `Oops`****含义****典型日志****可能原因****处理建议****2. `panic`****含义****典型日志****可能原因****处理建议****3. `BUG`****含义****典型日志****可能原因****处理建议****4. `kernel NULL pointer`****含义****典型日志****可能原因****处理建议****5. `WA…

Linux驱动开发2:字符设备驱动

Linux驱动开发2&#xff1a;字符设备驱动 字符设备驱动开发流程 字符设备是 Linux 驱动中最基本的一类设备驱动&#xff0c;字符设备就是一个一个字节&#xff0c;按照字节流进行读写操作的设备&#xff0c;读写数据是分先后顺序的。比如最常见的点灯、按键、 IIC、 SPI&#x…

RuoYi-Cloud 验证码处理流程

以该处理流程去拓展其他功能模块处理流程&#xff0c;进而熟悉项目开发代码一、思路JavaWeb流程主干线&#xff1a;发起请求、处理请求、响应请求二、登录页面在登录页面按键F12打开开发者工具&#xff0c;点击network&#xff0c;刷新页面&#xff0c;点击code&#xff0c;查看…

云计算三大服务模式深度解析:IaaS、PaaS、SaaS

架构本质&#xff1a;云计算服务模式定义了资源抽象层级和责任分担边界&#xff0c;形成从基础设施到应用的全栈服务金字塔。三种模式共同构成云计算的服务交付模型核心框架。一、服务模式全景图 #mermaid-svg-f0Klw2fbuhBQqJTh {font-family:"trebuchet ms",verdana…

【sql学习之拉链表】

1.拉链表理解 记录历史。记录一个事物从开始&#xff0c;一直到当前状态的所有变化的信息。字段说明&#xff1a; start_dt&#xff1a;该条记录的生命周期开始时间 end_dt&#xff1a;该条记录的生命周期结束时间 end_dt’9999/12/31’表示该条记录目前处于有效状态 如果查询当…

STM32中实现shell控制台(shell窗口输入实现)

文章目录 一、总体结构二、串口接收机制三、命令输入与处理逻辑四、命令编辑与显示五、历史命令管理六、命令执行七、初始化与使用八、小结在嵌入式系统开发中,使用串口Shell控制台是一种非常常见且高效的调试方式。本文将基于STM32平台,分析一个简洁但功能完整的Shell控制台…

区分三种IO模型和select/poll/epoll

部分内容来源&#xff1a;JavaGuide select/poll/epoll 和 三种IO模型之间的关系是什么&#xff1f;区分普通IO和IO多路复用普通IO&#xff0c;即一个线程对应一个连接&#xff0c;因为每个线程只处理一个客户端 socket&#xff0c;目标明确&#xff1a;线程中直接操作该 socke…

Actor-Critic重要性采样原理

目录 AC的数据低效性&#xff1a; 根本原因&#xff1a;策略更新导致数据失效 应用场景&#xff1a; 1. 离策略值函数估计 2. 离策略策略优化 3. 经验回放&#xff08;Experience Replay&#xff09; 4. 策略梯度方法 具体场景分析 场景1&#xff1a;连续策略更新 场…

【赠书福利,回馈公号读者】《智慧城市与智能网联汽车,融合创新发展之路》

「5G行业应用」公号作家团队推出《智慧城市与智能网联汽车&#xff0c;融合创新发展之路》。本书由机械工业出版社出版&#xff0c;探讨如何通过车城融合和创新应用&#xff0c;促进汽车产业转型升级与生态集群发展&#xff0c;提升智慧城市精准治理与出行服务效能。&#xff0…

5G NR PDCCH之处理流程

本节主要介绍PDCCH处理流程概述。PDCCH&#xff08;Physical Downlink Control Channel&#xff0c;物理下行控制信道&#xff09;主要用于传输DCI&#xff08;Downlink Control Information&#xff0c;下行控制信息&#xff09;&#xff0c;用于通知UE资源分配&#xff0c;调…

力扣网编程135题:分发糖果(贪心算法)

一. 简介本文记录力扣网上涉及数组方面的编程题&#xff1a;分发糖果。这里使用贪心算法的思路来解决&#xff08;求局部最优&#xff0c;最终求全局最优解&#xff09;&#xff1a;每个孩子只需要考虑与相邻孩子的相对关系。二. 力扣网编程135题&#xff1a;分发糖果&#xff…

每日mysql

什么是Mysql索引最左匹配原则&#xff1f;最左匹配原则是指&#xff0c;在复合索引中&#xff0c;查询条件需要从左到右和索引开始依次完全匹配的时候&#xff0c;复合索引才可以被有效使用。因为联合索引在建立b树的过程中是根据索引的顺序从左到右进行排序的&#xff0c;所以…

树莓派5-ollama-linux-arm64.tgz 下载

1.下载 由于官方下载速度太慢且容易失败&#xff0c;我这里上传了一份到云盘供大家下载&#xff1a; 通过网盘分享的文件&#xff1a;ollama-linux-arm64.tgz 链接: https://pan.baidu.com/s/1tx_OPpl-8O2HJfXlP4tXTg?pwdffwx 提取码: ffwx --来自百度网盘超级会员v4的分享 …

2024年团体程序设计天梯赛

比赛链接 https://ac.nowcoder.com/acm/contest/80027 A&#xff1a; JMU-1 考察搜索的能力百度一下可知&#xff0c;2024 年天梯赛总决赛的比赛日为4 月 20日 参考代码 //2024 年天梯赛总决赛的比赛日为4 月 20日 void solve(){//A20-7cout<<"H\n"; } B&…