第五篇:明確拒絕不想編譯器自動生成的拷貝建構函式和賦值運算子過載函式

穆晨發表於2017-01-27

前言

       如果你不想要編譯器幫你自動生成的拷貝機制 (參考前文),那麼你應當明確的拒絕。

       如何拒絕?這便是本文要解決的主要問題。

問題描述

       當你定義了一個類,而這個類中各物件之間也是封裝的 - 禁止同類物件之間的相互賦值以及複製,那麼你需要遮蔽掉編譯器幫你生成的拷貝建構函式以及賦值運算子。

       在許多程式碼中,會看到通過將拷貝建構函式和賦值運算子過載函式宣告為私有且不予實現來實現這個功能。然而,這不是最科學的做法。

       因為這沒有做到真正的遮蔽:你在自己的成員函式中,或者友元函式中仍然可以呼叫這兩個私有函式,編譯器將不會報錯。

       也許你會狡辯,這種做法的私有拷貝建構函式以及賦值運算子過載函式都沒有實現,編譯器會報錯。但,報錯的不是編譯器,是連結器。優質的程式碼應當將錯誤儘可能的移至編譯期。

科學的做法

       首先專門為阻止拷貝動作設計一個類,如下:

1 class Uncopyable {
2 protected:
3     Uncopyable() {}    // 允許派生類物件構造,析構。
4     ~Uncopyable() {}
5 private:
6     Uncopyable(const Uncopyable &);    // 但阻止拷貝
7     Uncopyable& operator=(const Uncopyable &);
8 };

       如果你需要遮蔽掉某個類的拷貝機制,需要做的只是私有繼承該類:

1 1 class A : private Uncopyable {
2 2     //......
3 3 };

       類 A 的拷貝函式被順利遮蔽掉了。當 A 類物件發生拷貝或賦值,必然會先去執行其父類 Uncopyable 類的拷貝建構函式或過載賦值運算子函式。但這兩個函式在 Uncopyable 中已經被宣告為私有了,因此編譯器會不通過。

小結

       這種做法的科學依據在於,目標類的成員函式或者友元函式也都無法呼叫被遮蔽的拷貝建構函式和賦值運算子過載函式。

       如果呼叫發生,報錯的也是編譯器,而非連結器。

相關文章