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

C++11 = delete 深度解析:禁用預設函數與更多用法

     編輯:關於C++

引言:編譯器隱式生成的特殊成員函數

在 C++ 中,若類別未顯式宣告,編譯器會隱式生成六個特殊成員函數(C++98 四個,C++11 新增兩個移動語義函數):

  • 預設建構函式
  • 解構函式
  • 拷貝建構函式
  • 拷貝指派運算子
  • 移動建構函式
  • 移動指派運算子

這些函數預設為 `public` 且 `inline`,實作為成員逐一拷貝/移動。但有時我們需要禁止某些操作(如不可複製的資源管理類別),C++98 與 C++11 提供了截然不同的解決方案。

C++98 時代的妥協:私有化且不實作

傳統做法是將拷貝建構函式與拷貝指派運算子宣告為 `private` 且不提供定義:

class NonCopyable {
private:
    NonCopyable(const NonCopyable&);            // 僅宣告,不定義
    NonCopyable& operator=(const NonCopyable&); // 僅宣告,不定義
};

若程式碼嘗試複製,會在連結階段報錯(link-time error)。為了將錯誤提前到編譯期,常引入基類 `Uncopyable` 並採用私有繼承:

class Uncopyable {
protected:
    Uncopyable() {}
    ~Uncopyable() {}
private:
    Uncopyable(const Uncopyable&);
    Uncopyable& operator=(const Uncopyable&);
};

class Leaf : private Uncopyable { /* ... */ };

此技巧雖有效,但語法繁瑣,且錯誤訊息往往晦澀難懂。

C++11 革命:= delete 與 = default

C++11 引入刪除函數語法,只需在宣告後加上 `= delete` 即可明確禁用:

class Leaf {
public:
    Leaf() = default;           // 顯式要求編譯器生成預設版本
    ~Leaf() = default;
    Leaf(const Leaf&) = delete;           // 禁用拷貝建構
    Leaf& operator=(const Leaf&) = delete; // 禁用拷貝指派
};

優勢顯而易見:

  1. 編譯期報錯,訊息明確指出 "use of deleted function"。
  2. 語法簡潔,意圖清晰,無需額外基類。
  3. 可選擇性保留預設行為(`= default`)或完全禁用。

= delete 的擴展應用:不止於特殊成員函數

C++11 允許對任意函數使用 `= delete`,包括非成員函數、函數模板特化等,極大擴展了用途。

3.1 禁用特定多載:類型安全的介面設計

例如 `isLucky` 函數僅接受 `int`,拒絕 `char`、`bool`、`double` 等隱式轉換:

bool isLucky(int number);           // 唯一允許的多載
bool isLucky(char) = delete;        // 拒絕字元
bool isLucky(bool) = delete;        // 拒絕布林
bool isLucky(double) = delete;      // 拒絕浮點數

// 調用示例
isLucky(7);    // OK
isLucky('a');  // 編譯錯誤:call to deleted function
isLucky(true); // 編譯錯誤
isLucky(3.14); // 編譯錯誤

這比 `static_assert` 或執行期檢查更優雅,直接在編譯期阻擋不當類型。

3.2 禁用模板特化:解決類別作用域限制

C++98 中,若想禁用類別模板的特定特化(如 `processPointer`),無法在類別內宣告 `private` 特化(模板特化必須在命名空間作用域)。C++11 允許在類別外直接刪除:

class Widget {
public:
    template <typename T>
    void processPointer(T* ptr) { /* ... */ }
};

// 在命名空間作用域刪除 void* 特化
template <>
void Widget::processPointer<void>(void*) = delete;

此時調用 `processPointer(static_cast(nullptr))` 會直接編譯失敗,完美解決了 C++98 的作用域難題。

最佳實踐總結

  1. 優先使用 `= delete` 而非私有未定義:錯誤更早、更清晰、語法更簡潔。
  2. 任何函數皆可刪除:非成員函數、模板具現化、特化版本均適用。
  3. 搭配 `= default` 使用:明確表達「我要預設行為」或「我要禁用」,讓類別介面意圖一目瞭然。

`= delete` 是 C++11 強化類型安全、簡化資源管理類別設計的利器,現代 C++ 開發應熟練掌握並廣泛應用。

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