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

C++ 巨集實現單例模式:兩種宏封裝與使用範例

     編輯:關於C語言

單例模式確保類全局僅有一實例並提供訪問點。C++ 中常用 Meyers Singleton (函數內靜態變量) 實現線程安全 (C++11 保證) 的單例。本文提供兩個巨集,將單例樣板代碼封裝為一鍵引入,區別在於是否預設實現無參構造函數,並展示實際類整合範例。

一、兩個單例巨集定義

1. 無預設構造函數版 — 適合需自定義構造邏輯


#define SINGLETON_CLASS_NO_DEF_CONSTRUCT_BODY(class_name) \
private: \
    class_name(); \
    class_name(const class_name&); \
    class_name& operator=(const class_name&); \
public: \
    static class_name& Instance() { \
        static class_name one; \
        return one; \
    }

特點:聲明私有構造/拷買/賦值,**不提供定義**,強制使用者在 `.cpp` 中實現構造函數。

2. 帶預設構造函數版 — 適合無狀態/簡單初始化類


#define SINGLETON_CLASS(class_name) \
private: \
    class_name() {} \
    class_name(const class_name&); \
    class_name& operator=(const class_name&); \
public: \
    static class_name& Instance() { \
        static class_name one; \
        return one; \
    }

特點:內聯定義空構造函數 `class_name() {}`,使用者**無需**在 `.cpp` 實現構造。

二、核心機制解析

  • Meyers Singleton:`static class_name one` 在函數內部靜態存儲期,C++11 起初始化線程安全,無需額外鎖。
  • 禁止拷貝/賦值:私有化拷貝構造與賦值運算符 (C++11 可改用 `= delete`)。
  • 返回引用:`Instance()` 返回引用,避免拷貝,確保單一性。
  • 巨集衛生:反斜線換行、無分號結尾,調用處需加分號 `SINGLETON_CLASS(MyClass);`。

三、實戰整合範例

場景:主機基礎資訊管理器 — 需自定義構造邏輯

頭文件 `HostBasicInformation.h`


#ifndef HOST_BASIC_INFORMATION_H
#define HOST_BASIC_INFORMATION_H

#include 

class HostBasicInformation {
    // 引入無預設構造版巨集
    SINGLETON_CLASS_NO_DEF_CONSTRUCT_BODY(HostBasicInformation)

public:
    virtual ~HostBasicInformation();

    std::string GetInstallCode();
    // 其他業務方法...
};

#endif // HOST_BASIC_INFORMATION_H

實現文件 `HostBasicInformation.cpp`


#include "HostBasicInformation.h"

// 必須實現構造函數 (巨集僅聲明)
HostBasicInformation::HostBasicInformation() {
    // 初始化邏輯:讀取註冊表、硬體資訊、生成安裝碼等
}

HostBasicInformation::~HostBasicInformation() {
    // 清理資源
}

std::string HostBasicInformation::GetInstallCode() {
    return "INSTALL_CODE_12345"; // 實際業務邏輯
}

使用端代碼


#include "HostBasicInformation.h"

void someFunction() {
    // 獲取單例引用並調用方法
    std::string code = HostBasicInformation::Instance().GetInstallCode();
    // 等價於:
    // HostBasicInformation &inst = HostBasicInformation::Instance();
    // std::string code = inst.GetInstallCode();
}

四、巨集優缺點評析

優點缺點/風險
極度簡化聲明,一鍵引入完整單例骨架巨集無作用域,污染全局命名空間
強制統一實現風格,防止手寫疏漏 (如忘記私有化構造)調試困難:編譯器報錯指向巨集展開行,非邏輯源頭
編譯期展開,零運行開銷模板/CRTP 方案類型安全性更高,支援繼承鏈單例
適配舊標準 (C++98/03) 專案現代 C++ (C++17) 推薦內聯靜態成員變量 + `delete` 拷貝

五、現代 C++ (C++17) 推薦寫法:無巨集、類型安全


class ModernSingleton {
private:
    ModernSingleton() = default;
    ~ModernSingleton() = default;
    ModernSingleton(const ModernSingleton&) = delete;
    ModernSingleton& operator=(const ModernSingleton&) = delete;

public:
    static ModernSingleton& Instance() {
        static ModernSingleton instance;
        return instance;
    }

    void DoSomething() { /* ... */ }
};

// 使用
ModernSingleton::Instance().DoSomething();

優勢:無巨集、編譯器完整類型檢查、支援繼承 (CRTP 模式)、IDE 重構友好。

六、適用建議

  1. 維護舊專案 (C++98/03/11):巨集可接受,但建議漸進式遷移至現代寫法。
  2. 新專案 (C++14/17/20):直接寫現代寫法,拒絕巨集。
  3. 需參數化構造的單例:Meyers 模式不適用,需工廠函數 + `std::call_once` 或依賴注入容器管理。
  4. 跨 DLL 邊界單例:極其棘手 (各模組獨立靜態存儲),避免跨 DLL 使用單例,改用傳遞引用/指標。

巨集單例是特定歷史階段的產物,理解其原理有助於閱讀舊代碼,但現代 C++ 開發應優先選擇類型安全、工具友好的原生語法實現。

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