Effective Modern C++ 條款7:區分使用 `()` 和 `{}` 創建對象

在 C++11 及以后的版本中,初始化對象的方式變得更加靈活,但也帶來了選擇上的困惑。(){} 是兩種常見的初始化語法,它們在語義、行為和適用場景上有顯著差異。本文將通過具體示例,深入解析這兩種初始化方式的區別,并探討如何在實際編程中合理選擇。


一、基本區別:(){} 的語義差異

1.1 ():傳統構造函數調用

Widget w1(10);          // 調用帶一個 int 參數的構造函數
Widget w2(10, true);    // 調用帶 int 和 bool 參數的構造函數
  • 語義:直接調用構造函數,適用于已知參數類型的場景。
  • 特點:允許隱式類型轉換(如 intdouble),但可能引發“最令人頭疼的解析”(Most Vexing Parse)問題。

1.2 {}:統一初始化(Uniform Initialization)

Widget w3{10};          // 同樣調用帶一個 int 參數的構造函數
Widget w4{10, true};    // 同樣調用帶 int 和 bool 參數的構造函數
  • 語義:使用花括號初始化,適用于所有初始化場景(包括數組、容器、對象等)。
  • 特點:禁止隱式變窄轉換(如 doubleint),并且能避免“最令人頭疼的解析”問題。

二、關鍵差異:(){} 的行為對比

2.1 避免“最令人頭疼的解析”(Most Vexing Parse)

Widget w1(10);          // 正確:調用帶一個 int 參數的構造函數
Widget w2();            // 錯誤!被解析為函數聲明,而非對象創建
Widget w3{};            // 正確:調用默認構造函數
  • 問題:Widget w2(); 會被編譯器視為一個返回 Widget 類型的函數聲明,而不是創建對象。
  • 解決方案:使用 {} 避免這種歧義。

2.2 禁止隱式變窄轉換

double d = 3.14;
int a{d};               // 錯誤!禁止將 double 轉換為 int
int b(d);               // 正確!允許隱式轉換
  • 原因:花括號初始化會檢查類型轉換是否會導致數據丟失(如 doubleint),而圓括號允許隱式轉換。

2.3 與 std::initializer_list 的交互

class Widget {
public:Widget(int i, bool b);              // 構造函數Widget(std::initializer_list<int> il); // std::initializer_list 構造函數
};Widget w1(10, true);        // 調用 int/bool 構造函數
Widget w2{10, true};        // 調用 std::initializer_list 構造函數
  • 問題:如果類中存在 std::initializer_list 構造函數,花括號初始化會優先選擇它,即使其他構造函數更匹配。
  • 示例:
    class Widget {
    public:Widget(int i, bool b);              // 構造函數Widget(std::initializer_list<int> il); // std::initializer_list 構造函數
    };Widget w{10, true};         // 調用 std::initializer_list 構造函數(10 和 true 被轉換為 int)
    

三、典型應用場景與陷阱

3.1 std::vector 的初始化差異

std::vector<int> v1(10, 20);    // 創建 10 個元素,值為 20
std::vector<int> v2{10, 20};    // 創建 2 個元素,值為 10 和 20
  • 關鍵區別:() 用于指定元素數量和初始值,{} 用于直接初始化元素列表。

3.2 模板中的初始化選擇

template<typename T, typename... Args>
void createObject(Args&&... args) {T obj1(std::forward<Args>(args)...);  // 使用圓括號T obj2{std::forward<Args>(args)...};  // 使用花括號
}
  • 問題:在模板中,(){} 的行為可能影響構造函數的選擇。例如:
    createObject<std::vector<int>>(10, 20);
    // obj1: std::vector<int> 有 10 個元素,值為 20
    // obj2: std::vector<int> 有 2 個元素,值為 10 和 20
    

3.3 auto 與花括號的類型推導

auto x{10};        // x 的類型是 std::initializer_list<int>
auto y(10);        // y 的類型是 int
  • 注意:使用花括號初始化 auto 時,類型會被推導為 std::initializer_list,而非原始類型。

四、最佳實踐與建議

4.1 優先使用 {} 的場景

  • 需要防止隱式變窄轉換:如將 doubleint
  • 避免“最令人頭疼的解析” :如創建對象時避免誤將代碼解析為函數聲明。
  • 統一初始化語法:適用于所有初始化場景(如容器、對象、數組等)。

4.2 保留 () 的場景

  • 兼容 C++98 代碼:保持與舊代碼的語法一致性。
  • 明確調用特定構造函數:如 std::vectorsizevalue 初始化。
  • 避免 std::initializer_list 的意外調用:當類中存在多個構造函數時,避免因花括號初始化導致的歧義。

4.3 模板中的權衡

  • 模板參數傳遞:根據實際需求選擇 (){},避免因初始化方式不同導致邏輯錯誤。
  • 文檔說明:在模板庫中明確說明初始化方式的選擇依據。

五、總結

  • () 是傳統的構造函數調用方式,允許隱式轉換,但可能引發“最令人頭疼的解析”。
  • {} 是統一初始化語法,禁止隱式變窄轉換,能避免函數聲明歧義,但可能優先選擇 std::initializer_list 構造函數。
  • 選擇建議:優先使用 {} 以提升代碼安全性,但在需要明確調用特定構造函數或兼容舊代碼時,保留 ()

通過理解這兩種初始化方式的差異,開發者可以更靈活地編寫健壯、清晰的 C++ 代碼,避免潛在的陷阱。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/88443.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/88443.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/88443.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java基礎-String常用的方法

String常用的三種構造方法 public static void main(String[] args) {//1.使用常量字符串構造String s1 "1.Hello world";System.out.println(s1);//2.使用new關鍵字構造String s2 new String("2.Hello world");System.out.println(s2);//3。使用字符數組…

數學建模:多目標規劃:ε約束法、 理想點法

一、ε約束法定義ε約束法通過將部分目標函數轉化為約束條件&#xff0c;保留一個主要目標進行優化。1、選擇一個主要目標 fk?(x) 進行優化。2、其他目標 fi?(x) 轉化為約束 fi?(x)≤εi?&#xff0c;其中 εi? 是決策者設定的容許閾值。??原理????目標選擇??&…

linux kernel struct regmap_config結構詳解

在 Linux 內核中&#xff0c;struct regmap_config 是 ?Regmap 子系統的核心配置結構體&#xff0c;用于定義如何與底層硬件寄存器進行交互。Regmap&#xff08;Register Map&#xff09;子系統通過抽象不同總線&#xff08;如 I2C、SPI、MMIO 等&#xff09;的寄存器訪問細節…

【Python3教程】Python3高級篇之CGI編程

博主介紹:?全網粉絲23W+,CSDN博客專家、Java領域優質創作者,掘金/華為云/阿里云/InfoQ等平臺優質作者、專注于Java技術領域? 技術范圍:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大數據、物聯網、機器學習等設計與開發。 感興趣的可…

docker安裝Consul筆記

安裝過程 詳細步驟如下&#xff1a; 首先拉取Consul的Docker鏡像&#xff1a; docker pull hashicorp/consul:1.18.1創建Consul的配置文件和數據目錄&#xff1a; mkdir -p /srv/docker/consul/data mkdir -p /srv/docker/consul/config在config目錄下創建一個config.json配置文…

.net數據脫敏

.NET數據脫敏技術&#xff1a;保障數據安全的有效手段 在當今數字化時代&#xff0c;數據安全至關重要。尤其是涉及到用戶的敏感信息&#xff0c;如密碼、手機號碼等&#xff0c;必須采取有效的措施進行保護。數據脫敏就是這樣一種技術&#xff0c;它能夠在不影響數據可用性的…

【openp2p】 學習2:源碼閱讀P2PNetwork和P2PTunnel

【openp2p】 學習1:P2PApp和優秀的go跨平臺項目已經做了初步分析。閱讀原版工程,感覺工程是一個暴露內網服務端口,讓外部可以用的一個實現是一個完整的、跨平臺的可商業化的應用。感謝作者需要學習作者的設計思路工程構建 F:\GolandProjects\openp2p\core\p2pnetwork.go通常…

網安學習NO.14

防火墻基礎實驗 傳統防火墻配置實驗拓撲圖PC&#xff1a; ip 192.168.10.1 255.255.255.0 192.168.10.254 ip dns 114.114.114.114二層交換機 vl 10 ex int e0/0 sw mo ac sw ac vl 10 ex inr e0/1 sw tr en do sw mo tr三層交換機 vl 10 ex int g0/0 sw tr en do sw mo tr ex …

ESP32語音喚醒

兩種喚醒方式AfeWakeWord與EspWakeWord對比 底層技術 AfeWakeWord&#xff1a;基于ESP-IDF的AFE框架&#xff08;esp_afe_sr_iface_t&#xff09;&#xff0c;高性能模式&#xff08;AFE_MODE_HIGH_PERF&#xff09;EspWakeWord&#xff1a;基于WakeNet接口&#xff08;esp_wn_…

借助 Wisdom SSH AI 助手,輕松安裝 CentOS 8 LNMP 環境

打開Wisdom SSH軟件&#xff0c;在AI對話區輸入“在CentOS 8服務器安裝LNMP環境”&#xff0c;AI助手會按以下步驟分析并執行安裝&#xff1a; 安裝Nginx 分析&#xff1a;CentOS 8默認軟件源可能沒有Nginx&#xff0c;所以要先啟用Nginx官方軟件源&#xff0c;然后才能安裝Ngi…

WD0407 40V 7A 超級肖特基二極管,應用于開關汽車工業控制

WD0407 40V 7A 超級肖特基二極管說明? 產品概述? WD0407 是一款性能卓越的超級肖特基二極管&#xff0c;專為滿足現代電子設備對高效、可靠電源管理的需求而設計。它采用先進的半導體制造工藝&#xff0c;在諸多關鍵性能指標上表現出色&#xff0c;能夠為各類電路提供穩定、高…

盧比危機下的金融破局:科倫坡交易所技術升級作戰圖

&#x1f30f; 今日南亞風暴眼 印度雙重上市機制加速落地&#xff1a;印度國家證券國際交易所&#xff08;NSE IX&#xff09;與科倫坡證券交易所&#xff08;CSE&#xff09;達成技術對接協議&#xff0c;斯企可通過印度GIFT City吸引美元資本&#xff0c;交易時段覆蓋全球22小…

upload-labs靶場通關詳解:第20關 /.繞過

一、分析源代碼// 初始化上傳狀態標記&#xff0c;默認為false&#xff0c;即文件未上傳 $is_upload false; // 初始化消息變量&#xff0c;用于存儲錯誤信息 $msg null;// 檢查是否通過POST方式提交了表單&#xff08;點擊上傳按鈕&#xff09; if (isset($_POST[submit])) …

企業用云狀態評估

云部署形態及其策略規劃成熟度 單云部署&#xff1a; 主要業務負載運行在單一公有云或私有云上 多云/混合云部署 —有清晰戰略規劃與實施&#xff1a; 業務負載運行在多個云&#xff08;公有云或混合云&#xff09;上&#xff0c;并且企業擁有清晰的多云/混合云戰略規劃&#x…

STM32G473串口通信-USART/UART配置和清除串口寄存器狀態的注意事項

USART和UART配置的區別 如果USART使用的是異步通信&#xff0c;那么UART與USART配置基本相同。 USART配置如下:UART配置如下&#xff1a;如果USART使用的是同步通信&#xff0c;那么UART配置就有差異。首先通信雙方都是使用USART的同步通信&#xff0c;一個主機&#xff0c;一個…

Debezium:一款基于CDC的開源數據同步工具

Debezium 是由 Red Hat 開源的一種基于變更數據捕獲&#xff08;CDC&#xff09; 的分布式平臺&#xff0c;專為實時捕獲和傳播數據庫的變更事件而設計。Debezium 常見的使用場景包括&#xff1a; 實時數據集成&#xff1a;將數據庫變更同步到數據倉庫或數據湖&#xff0c;支撐…

從面向對象編程語言PHP轉到Go時的一些疑惑?

前言 1、php中面向對象編程時 與 Go中的區別&#xff1f; 2、php中最常使用laravel框架&#xff0c;不用過多關注依賴注入和反射&#xff0c;在go中又該如何使用呢&#xff1f;是 舍棄&#xff1f; 本文是一個系統化梳理&#xff0c;幫助從 語言哲學 → 依賴注入在 Go 的現狀 →…

Vue3中使用konva插件動態制作海報以及可在畫布上隨意移動位置

1、下載konva插件 官網地址 npm install vue-konva konva --save2、在主文件中引入&#xff0c;如main.js import VueKonva from vue-konva; app.use(VueKonva);3、組件內使用&#xff0c;我現在的布局是左側是畫布&#xff0c;右側是相關設置&#xff08;顏色、標題等&#…

政安晨【開源人工智能硬件】【ESP樂鑫篇】 —— 在macOS上部署工具開發環境(小資的非開發者用蘋果系統也可以玩樂鑫)

政安晨的個人主頁&#xff1a;政安晨 歡迎 &#x1f44d;點贊?評論?收藏 希望政安晨的博客能夠對您有所裨益&#xff0c;如有不足之處&#xff0c;歡迎在評論區提出指正&#xff01; 前言 開源人工智能硬件會給你帶來無限可能&#xff0c;玩開源硬件&#xff0c;環境和工具少…

Vue3 學習教程,從入門到精通,vue3學習中的JavaScript ES6 特性詳解與案例(5)

vue3學習中的JavaScript ES6 特性詳解與案例 ES6&#xff08;ECMAScript 2015&#xff09;是 JavaScript 的一個重要版本&#xff0c;引入了許多新特性&#xff0c;極大地提升了語言的表達能力和開發效率。本文將詳細介紹 ES6 的主要特性&#xff0c;包括 let 和 const 命令、變…