首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C++ 空類別的隱式成員函數:標準規定 vs 經典誤區

     編輯:關於C語言

前言:一個看似簡單的面試題

class Empty {};

這個空類別到底有哪些隱式成員函數?網路上流傳的「六大預設成員」說法:**預設建構子、拷貝建構子、解構子、拷貝指派運算子、取址運算子、const 取址運算子** —— 這是錯誤的。本文依據 ISO C++ 標準 (C++03/11/14/17/20) 釐清真相。

標準規定:只有四個 (C++98/03) 或五個 (C++11 起)

成員函數C++98/03C++11 起條件
預設建構子✓✓無使用者宣告任何建構子
拷貝建構子✓✓無使用者宣告拷貝建構子
拷貝指派運算子✓✓無使用者宣告拷貝指派
解構子✓✓無使用者宣告解構子
移動建構子✗✓*無使用者宣告拷貝/移動/解構子
移動指派運算子✗✓*同上

* 符合條件時隱式宣告為 defaulted,不滿足則隱式宣告為 deleted

關鍵澄清:取址運算子 & 不是隱式成員

// 錯誤說法:編譯器會隱式宣告
Empty* operator&();
const Empty* operator&() const;

// 正確:編譯器**不會**隱式宣告這兩個運算子
// 當對 Empty 物件使用 & 時,直接使用**內建運算子**

Scott Meyers 的承認與更正

《Effective C++》條款 45 原文錯誤列出取址運算子。讀者回報後,Meyers 在勘誤表確認:

"A class declaring no operator& function(s) does NOT have them implicitly declared. Rather, compilers use the built-in address-of operator whenever '&' is applied to an object of that type. This behavior is technically not an application of a global operator& function. Rather, it is a use of a built-in operator."

為什麼這很重要?

  1. 類別大小:空類別大小為 1 (確保不同物件有不同位址),非 0
  2. 運算子重載:若類別自訂 operator&,會屏蔽內建取址,導致 &obj 回傳自訂類型,標準庫容器、智慧指標可能失效
  3. 標準庫特徵:std::is_empty<Empty>::value == true 依賴無非靜態資料成員且無虛函數

C++11 後的隱式移動語意

class Empty {};

Empty a;
Empty b = std::move(a);  // 呼叫隱式移動建構子 (若符合條件)
Empty c;
c = std::move(b);        // 呼叫隱式移動指派

隱式移動操作會逐成員移動。空類別無成員,故移動操作等同於無操作 (no-op),但仍參與多載解析。

實務驗證程式碼

#include <iostream>
#include <type_traits>

class Empty {};

int main() {
    std::cout << "is_empty: " << std::is_empty<Empty>::value << '\n';
    std::cout << "has default ctor: " << std::is_default_constructible<Empty>::value << '\n';
    std::cout << "has copy ctor: " << std::is_copy_constructible<Empty>::value << '\n';
    std::cout << "has move ctor: " << std::is_move_constructible<Empty>::value << '\n';
    std::cout << "has copy assign: " << std::is_copy_assignable<Empty>::value << '\n';
    std::cout << "has move assign: " << std::is_move_assignable<Empty>::value << '\n';
    std::cout << "sizeof(Empty): " << sizeof(Empty) << '\n';
    
    // 驗證取址運算子非成員
    Empty e;
    Empty* p = &e;  // 使用內建 &
    (void)p;
}

輸出 (C++17):

is_empty: 1
has default ctor: 1
has copy ctor: 1
has move ctor: 1
has copy assign: 1
has move assign: 1
sizeof(Empty): 1

常見誤區總結表

說法正確性說明
空類別有 6 個隱式成員❌ 錯誤含取址運算子,出自舊版 Effective C++ 錯誤
空類別有 4 個隱式成員 (C++98)✅ 正確預設建構、拷貝建構、拷貝指派、解構
空類別有 6 個隱式成員 (C++11+)⚠️ 部分正確上述 4 個 + 移動建構 + 移動指派 (視條件)
取址運算子是隱式成員❌ 錯誤使用內建運算子,非成員函數
自訂 operator& 會取代內建✅ 正確極度危險,破壞標準庫假設

結語

「空類別有六大預設成員」是流傳久遠的錯誤迷思。依標準,**只有建構/解構/指派操作會被隱式宣告,取址運算子永遠使用內建版本**。理解此區別,不僅能在面試中加分,更能避免在實務中誤重載 operator& 導致難以診斷的標準庫相容性問題。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved