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

C++ Union 聯合體深度解析:C 風格慣例、構造函數限制、匿名聯合、類型懲罰、記憶體佈局與最佳實踐

     編輯:更多程式設計知識

前言

union 是 C/C++ 中「多類型共享同一塊內存」的機制。C++ 擴展允許成員函數、構造函數,但**強烈建議堅持 C 風格慣例**:無構造/析構/拷貝/賦值/虛函數,僅作 POD 類型複用內存。本文系統講解限制原因、正確用法、三大經典模式(重命名、壓縮、類型懲罰)、匿名聯合、判別式模式。

為何 C++ 限制聯合體含構造函數的類?

  • 空間分配時機:聯合體只分配原始內存,**不調用構造函數**。若成員有構造函數,編譯器無法決定何時調用(創建聯合體時?首次訪問該成員時?)。
  • 析構函數難題:聯合體銷毀時不知哪個成員處於活躍狀態,無法正確調用析構函數,導致資源洩漏或雙重釋放。
  • 虛函數表指針:含虛函數的類有隱藏 vptr,佔用內存並要求構造時初始化,與聯合體「原始內存」語義衝突。
  • 標準規定:C++11 起允許「平凡構造/析構」的類型進入聯合體,但非平凡類型仍禁止。

正確定義模式(C 風格)

// ✅ 推薦:純數據成員,無構造/析構/虛函數
union DataUnion {
    const char* str;
    long        num;
    struct { float x, y, z, w; } vec4;
    float       arr[4];
};

class Wrapper {
    DataUnion data_;
public:
    Wrapper(const char* s) { data_.str = s; }
    Wrapper(long n)        { data_.num = n; }
    // 手動管理活躍成員,必要時加判別式 enum { STR, NUM } tag_;
};

三大經典應用模式

1. 重命名:同一內存多語義訪問

struct Vec4 {
    union {
        struct { float x, y, z, w; };
        struct { float r, g, b, a; };
        float v[4];
    };
};
// 用法:v.x / v.r / v[0] 指向同一 float

2. 壓縮:結構體整體比較/複製/傳輸

struct Compressed {
    bool operator==(const Compressed& o) const { return word == o.word; }
    union {
        struct { char a,b,c,d,e,f,g; };
        long long word; // 64 位一次比較 7 個字節
    };
};

3. 類型懲罰:同一位模式按不同類型解讀

union FloatInt {
    float f;
    int   i;
};
FloatInt u; u.f = 1.0f;
int bits = u.i; // 0x3F800000 (IEEE 754 表示)
// 用途:快速 float↔int 轉換、提取指數/尾數、自定義哈希

匿名聯合

void foo() {
    union { int i; float f; }; // 無標籤,直接訪問 i、f
    i = 42;
    f = 3.14f; // 覆蓋 i
}
// 限制:無成員函數、無私有/保護成員、全局需 static 或置於匿名命名空間

判別式模式:運行時知道活躍成員

struct TaggedUnion {
    enum Type { INT, STR } type;
    union {
        int    i;
        char*  s;
    };
    TaggedUnion(int v)    : type(INT), i(v) {}
    TaggedUnion(const char* v) : type(STR), s(strdup(v)) {}
    ~TaggedUnion() { if (type==STR) free(s); }
    // 訪問器模式
    int getInt() const { assert(type==INT); return i; }
};

移植性與危險提示

  • 端序/對齊/類型大小:跨平台(32/64 位、大/小端)時聯合體佈局不同,**嚴禁用於序列化/網絡傳輸/持久化**,除非手動定義佈局(#pragma pack、固定寬度類型)。
  • 嚴格別名規則:C++ 標準規定通過非活躍成員讀取屬於未定義行為 (UB)。現代編譯器 (GCC/Clang/MSVC) 在優化時會假設不同類型指針不指向同一地址,**類型懲罰在高優化級別可能失效**。
  • 安全替代:std::memcpy、std::bit_cast (C++20)、union 僅在同一編譯單元、低優化、嵌入式場景可控使用。

總結

Union 是「內存複用」的鋒利工具。**堅持 C 風格 POD 成員、手動管理活躍狀態、避免跨平台序列化、警惕嚴格別名規則**,即可在嵌入式、遊戲引擎、高性能序列化、網絡協議解析等場景安全發揮威力。現代 C++ 優先考慮 std::variant (C++17) 獲得類型安全的和類型。

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