編譯器默認生成的c++類六大成員函數

編譯器默認生成的c++類六大成員函數

編譯器默認生成的六大成員函數

當你定義一個空類時,例如:

class Empty {};

如果代碼中沒有顯式定義任何成員函數,C++編譯器會在需要時(例如,代碼中實際調用了這些函數)為你自動生成以下六個特殊成員函數(Special Member Functions):

  1. 默認構造函數 (Default Constructor): Empty();

    • 用于創建對象,當沒有提供任何初始化參數時被調用。
    • 例如:Empty e1;
  2. 析構函數 (Destructor): ~Empty();

    • 用于對象銷毀,在對象生命周期結束時被調用。
    • 它負責清理資源,雖然對于空類來說沒什么可清理的。
  3. 拷貝構造函數 (Copy Constructor): Empty(const Empty& other);

    • 用于從一個同類對象創建新對象。
    • 例如:Empty e2(e1); 或 Empty e3 = e1;
  4. 拷貝賦值運算符 (Copy Assignment Operator): Empty& operator=(const Empty& other);

    • 用于將一個已存在的同類對象的值賦給另一個已存在的對象。
    • 例如:e2 = e1;
  5. 移動構造函數 (Move Constructor) (C++11及以后): Empty(Empty&& other) noexcept;

    • 用于從一個右值(通常是臨時對象)“竊取”其資源來創建新對象,避免不必要的拷貝。
    • 例如:Empty e4(std::move(e1));
  6. 移動賦值運算符 (Move Assignment Operator) (C++11及以后): Empty&operator=(Empty&& other) noexcept;

    • 用于將一個右值對象的資源“竊取”并賦給一個已存在的對象。
    • 例如:e3 = std::move(e2);
#include <iostream>
#include <utility>class Empty {};int main() {std::cout << "創建一個默認對象 e1..." << std::endl;Empty e1;std::cout << "使用拷貝構造函數創建 e2..." << std::endl;Empty e2(e1);std::cout << "使用拷貝賦值運算符..." << std::endl;Empty e3;e3 = e2;std::cout << "使用移動構造函數創建 e4..." << std::endl;Empty e4(std::move(e1));std::cout << "使用移動賦值運算符..." << std::endl;e3 = std::move(e2);std::cout << "程序結束,對象將被銷毀。" << std::endl;return0;
}

這段代碼可以成功編譯和運行,這雄辯地證明了,即使我們沒有為 Empty 類編寫任何一個函數,編譯器也已經為我們提供了所有必要的“基礎設施”來完成對象的創建、拷貝、移動和銷毀。

類的六大成員函數生成規則及“三/五/零法則”

了解了“有什么”之后,一個優秀的工程師還應該了解“為什么有”和“什么時候沒有”。

生成規則

編譯器并非無腦生成這些函數。它的行為遵循一套精密的規則:

  • 只在需要時生成:這些函數只在它們被ODR-used(One Definition
    Rule-used,可以簡單理解為“被實際調用”)時,編譯器才會去定義它們。

  • 用戶優先:如果你顯式聲明了任何一個特殊成員函數(即使是用 =delete 或 =default),編譯器就不會再為該函數生成默認版本。

  • 復雜的關聯規則:聲明一個函數會“抑制”其他函數的自動生成。這正是“三/五法則”的核心。

    • 重要:一旦你聲明了自定義的析構函數、拷貝構造或拷貝賦值,編譯器將不會自動生成移動構造和移動賦值函數。這是因為自定義的析-構/拷貝行為可能與默認的移動行為不兼容。
    • 反之,聲明了移動操作,也會影響拷貝操作的自動生成。
    • 三法則 (Rule of Three): (C++03) 如果你顯式聲明了析構函數、拷貝構造函數或拷貝賦值運算符中的任何一個,通常意味著你需要同時管理這三者,因為類內可能含有需要深度拷貝的資源(如裸指針)。此時,編譯器將不再自動生成它認為你可能需要自己實現的拷貝操作。
    • 五法則 (Rule of Five): (C++11) 這是“三法則”的擴展。如果你聲明了上述三者中的任何一個,或者聲明了移動構造函數或移動賦值運算符,編譯器將認為你對資源管理有特殊意圖。

現代C++的智慧:“零法則” (Rule of Zero)

三/五法則”是C++開發者必須掌握的知識,但它們也反映了一種更底層的設計問題:手動資源管理。現代C++推崇**“零法則” (Rule of Zero)**。
核心思想:設計你的類,使其不需要編寫任何自定義的析構、拷貝/移動構造和賦值函數。將所有資源的所有權交給專門的資源管理類(如 std::string, std::vector, std::unique_ptr, std::shared_ptr)。

當你遵循“零法則”時,你的類(即使非空)也能像空類一樣,讓編譯器為其生成正確、高效的特殊成員函數。這些標準庫組件本身已經完美地實現了“五法則”,你的類只需組合它們,就能自動獲得正確的資源管理行為。這使得代碼更簡潔、更安全、更易于維護。

一個有趣的延伸:sizeof(Empty) 等于多少?

與空類相關的另一個高頻面試題是:sizeof(Empty) 的結果是多少?
答案不是0,而是1。
為什么是1?
C++標準規定,任何兩個不同的對象在內存中都必須有不同的地址。如果一個空類的大小為0,那么當你創建一個該類的數組時:

Empty arr[10];

&arr[0] 和 &arr[1] 的地址將會相同,這將導致指針算術和數組索引徹底失效。為了保證對象標識(identity)的唯一性,編譯器會為空類“填充”一個字節。這個字節不存儲任何有用的數據,僅僅是為了“占位”。

空基類優化 (Empty Base Optimization, EBO)

class BaseEmpty {}; // sizeof(BaseEmpty) == 1class Derived : public BaseEmpty {int x; // sizeof(int) == 4
};// sizeof(Derived) 通常是 4,而不是 1 + 4 = 5

在這種情況下,編譯器可以將空基類的“1字節”與派生類的成員(如 x)的數據存儲在相同的地址,或者說,讓空基類不占用任何額外的空間。這樣既滿足了“不同對象地址不同”的原則,又避免了內存浪費。這是C++零開銷原則 Zero-overhead principle的一個體現。

參考文獻:

面試官問“空類里有什么”?別再答“什么都沒有”了

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

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

相關文章

人工智能概念:常見的大模型微調方法

文章目錄一、微調技術的底層邏輯1.1 預訓練與微調的關系1.2 核心目標&#xff1a;適配任務與數據二、經典微調方法詳解2.1 全量微調&#xff08;Full Fine-Tuning&#xff09;2.2 凍結層微調&#xff08;Layer-Freezing Fine-Tuning&#xff09;2.3 參數高效微調&#xff08;Pa…

動態路由協議(一)

1. 動態路由 概述 靜態路由在大網絡里太麻煩&#xff08;設備多、配置量大&#xff0c;拓撲變了還要手動改&#xff09; 靜態路由是由工程師手動配置和維護的路由條目&#xff0c;命令行簡單明確&#xff0c;適用于小型或穩定的網絡。靜態路由有以下問題&#xff1a; 無法適…

LINUX812 shell腳本:if else,for 判斷素數,創建用戶

問題 [rootweb ~]# for((i2;i<n;i)) > if [ $n -ne $i ] && [ $((n%i)) -eq 0 ];then -bash: 未預期的符號 if 附近有語法錯誤 您在 /var/spool/mail/root 中有郵件 [rootweb ~]#[rootweb ~]# cat judgeprimeok.sh declare -i n read -p "please type the n…

游戲中角色持槍:玩家操控角色,角色轉向時槍也要轉向

角色持有槍&#xff0c;玩家&#xff08;你&#xff09;操控角色&#xff0c;那么&#xff0c;在角色轉向時&#xff0c;槍也要轉向。 先看看簡單情況&#xff1a;假定角色只面向左或右方向&#xff0c;pygame中用這句來實現&#xff1a;pos self.facing * self.gun_offset s…

深度學習入門Day8:生成模型革命——從GAN到擴散模型

一、開篇&#xff1a;創造力的算法革命從昨天的Transformer到今天的生成模型&#xff0c;我們正從"理解"世界邁向"創造"世界。生成對抗網絡(GAN)和擴散模型(Diffusion Model)代表了當前生成式AI的兩大主流范式&#xff0c;它們讓機器能夠生成逼真的圖像、音…

基于WRF-Chem的不同氣溶膠的輻射效應的研究

前言目前我對于氣溶膠輻射效應的理解就是設計敏感性實驗&#xff0c;基礎實驗打開氣溶膠參與輻射開關&#xff08;aer_ra_feedback&#xff09;&#xff0c;其他的實驗則關閉氣溶膠參與輻射過程開關&#xff0c;也有去掉某些氣溶膠的影響&#xff0c;如黑碳&#xff08;BC&…

專題:2025人形機器人與服務機器人技術及市場報告|附130+份報告PDF匯總下載

原文鏈接&#xff1a;https://tecdat.cn/?p43583 當特斯拉Optimus在工廠里精準分揀電池&#xff0c;當普渡機器人在酒店完成跨樓層配送&#xff0c;一個萬億級的智能革命正在拉開序幕。服務機器人與人形機器人不再是實驗室里的概念&#xff0c;而是正在重塑制造業、服務業的“…

JS 模塊化與打包工具

一、模塊化體系&#xff1a;ESM vs CJS 深入1.語法與靜態性(1)ESM:靜態語法&#xff0c;可被打包器做 Tree-shakingexport function play() {}export default ...import { play } from ./mod.js(2)CJS:運行時 require() , 分析能力弱&#xff0c;不利于 Tree-shaking2.Node 解析…

防御保護11

帶寬管理 --- 設備對自身的流量進行管理和控制&#xff0c;去提供帶寬保證、帶寬限制等等功能。 帶寬限制 帶寬保證 連接數限制 應用場景 實現帶寬管理 帶寬通道 --- 定義了被管理對象所能使用的帶寬資源 整體的保證帶寬和最大帶寬&#xff1b; SW1-SW2&#xff1a;VLAN 201 --…

[激光原理與應用-254]:理論 - 幾何光學 - 自動對焦的原理

自動對焦&#xff08;Auto Focus, AF&#xff09;是現代光學系統&#xff08;如相機、手機攝像頭、監控設備等&#xff09;的核心功能之一&#xff0c;其原理是通過檢測成像面的清晰度或測量物體距離&#xff0c;驅動透鏡組移動至最佳對焦位置。以下是自動對焦的詳細原理及技術…

【Python辦公】Mermaid代碼轉圖片工具 - Tkinter GUI版本

目錄 專欄導讀 項目簡介 功能特性 ?? 直觀的圖形界面 ?? 代碼編輯功能 ??? 圖片生成與預覽 ?? 文件操作 ? 性能優化 技術架構 核心技術棧 架構設計 安裝與使用 環境要求 依賴安裝 運行程序 使用步驟 代碼示例 基本流程圖 時序圖 甘特圖 核心代碼解析 1. 主類結構 2. …

【Activiti】要點初探

Activiti 7.0.0配置 流程配置節點流程XML流程部署部署后會操作表&#xff1a;&#xff08;每部署一次增加一條記錄&#xff09; ACT_RE_DEPLOYMENT 流程定義部署表 ACT_RE_PROCDEF 流程定義表 ACT_GE_BYTEARRAY 流程啟動查看任務&#xff08;張三要查看準備辦理任務&#xff0…

VBS 字符串處理

一. 字符串是由Unicode字符組成的一串字符。通常由數字&#xff0c;字母&#xff0c;符號組成。二. 常用函數1. 消除空格 Ltrim: 刪除字符串左側的空格。 Rtrim: 刪除字符串右側的空格。 trim: 刪除字符串左側和右側的空格。a" hello " b"sx"msgbo…

《算法導論》第 21 章-用于不相交集合的數據結構

引言不相交集合&#xff08;Disjoint Set&#xff09;&#xff0c;也稱為并查集&#xff08;Union-Find&#xff09;&#xff0c;是一種非常實用的數據結構&#xff0c;主要用于處理一些元素分組的問題。它支持高效的集合合并和元素查找操作&#xff0c;在很多算法中都有重要應…

基于51單片機RFID智能門禁系統紅外人流量計數統計

1 系統功能介紹 本設計基于STC89C52單片機&#xff0c;集成RFID讀卡器、紅外避障傳感器、繼電器、LCD1602液晶顯示和蜂鳴器&#xff0c;實現智能門禁與人流量統計功能。系統能夠識別合法的RFID卡開門&#xff0c;并實時統計通過人數&#xff0c;具有安全報警和直觀顯示功能。具…

c#,vb.net全局多線程鎖,可以在任意模塊或類中使用,但盡量用多個鎖提高效率

Public ReadOnly LockObj As New Object() 全局多線程鎖 VB.NET模塊中的LockObj 可以在任意模塊或類中使用吧 在 VB.NET 中&#xff0c;模塊&#xff08;Module&#xff09;中聲明的 Public ReadOnly LockObj 可以被其他模塊或類訪問和使用&#xff0c;但需要注意其可見性范圍…

企業安全運維服務計劃書

安全運維服務計劃書 一、概述 為保障企業信息系統安全、穩定、高效運行,防范各類網絡安全風險,提升整體安全防護能力,特制定本安全運維服務計劃書。本計劃旨在通過系統化、規范化的安全運維流程,全面識別、評估、處置并持續監控企業網絡環境中的安全風險,構建主動防御與…

小杰python高級(four day)——matplotlib庫

1.繪制子圖的方式pyplot中函數subplotFigure類中的函數add_subplotpyplot中函數subplotsfig, ax plt.subplots(nrows1, ncols1, *, sharexFalse, shareyFalse,squeezeTrue, subplot_kwNone, gridspec_kwNone, **fig_kw) 功能&#xff1a;繪制多個子圖&#xff0c;可以一次生成…

C# 編程out 參數需要在函數體內部初始化,然后引用的時候無需初始化

核心規則方法內部必須初始化&#xff1a;在方法體中&#xff0c;必須在方法返回前對 out 參數顯式賦值&#xff08;未賦值會導致編譯錯誤&#xff09;調用時無需初始化&#xff1a;調用方傳遞 out 參數前不需要初始化變量&#xff08;可直接使用未賦值的局部變量&#xff09;下…

【Redis在數據治理與數據隱私保護策略中的優化】

## Redis的自動補全功能&#xff1a;用戶體驗的無縫之助Redis作為一款高效的開源緩存數據庫&#xff0c;始終在用戶體驗優化方面走在前列。其自動補全功能的引入&#xff0c;為用戶帶來了全新的搜索體驗。這種功能不僅提升了搜索效率&#xff0c;更為用戶提供了更智能化的服務。…