【讀書筆記】《Effective Modern C++》第3章 Moving to Modern C++

《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&…