2014-12-01 76 views
5

我的理解是,這些語義僅用於複製構造函數,移動構造函數,複製指定,移動賦值和析構函數。使用= delete是爲了禁止使用其中一個函數,如果想讓編譯器知道在哪裏使用這些函數的默認值,則使用= default何時使用=默認vs =刪除

在製作課程時使用這些關鍵字的最佳做法是什麼?或者更確切地說,在開發一個課程時我怎麼才能注意到這些?

例如,如果我不知道是否使用這些功能之一,是否更好地使用delete來禁止它,或者允許它並使用default

+1

'= delete'可用於任何功能。 – Jarod42 2014-12-01 16:41:13

+2

如果您不知道是否使用其中一個功能,請_find out_。 – 2014-12-01 16:44:16

回答

4

好問題。

同樣重要的是:其中使用= default= delete

我對此有一些有爭議的建議。它與我們所知道的所有(包括我自己)在C++ 98/03中都是相互矛盾的。

開始類聲明與您的數據成員:

class MyClass 
{ 
    std::unique_ptr<OtherClass> ptr_; 
    std::string     name_; 
    std::vector<double>   data_; 
    // ... 
}; 

然後,儘可能接近實際,列出所有你想要明確地聲明六個特殊成員,並在可預知的順序(並且不要列出你希望編譯器處理的那些)。我更喜歡的順序是:

  1. 析構函數// this tells me the very most important things about this class.
  2. 默認構造函數
  3. 拷貝構造函數// I like to see my copy members together
  4. 拷貝賦值運算符
  5. 轉移構造// I like to see my move members together
  6. 移動賦值運算符

這個訂單的原因是:

  • 無論您默認的特殊成員,讀者更可能瞭解默認值如果知道數據成員是什麼,他們會做什麼。
  • 通過在接近頂部,一致的位置,並以一致的順序上市的特別會員,讀者更容易快速地實現其特殊的成員明確宣佈‐,因此無論是隱式聲明,或不根本不存在。
  • 通常都複製件(構造函數和賦值)是相似的。兩者都將默認爲 默認或刪除,顯式默認或刪除,或明確提供。很高興在兩行代碼中彼此相鄰確認這一點。
  • 通常都招成員(構造函數和賦值)類似於...

例如:

class MyClass 
{ 
    std::unique_ptr<OtherClass> ptr_; 
    std::string     name_; 
    std::vector<double>   data_; 
public: 
    MyClass() = default; 
    MyClass(const MyClass& other); 
    MyClass& operator=(const MyClass& other); 
    MyClass(MyClass&&) = default; 
    MyClass& operator=(MyClass&&) = default; 
    // Other constructors... 
    // Other public member functions 
    // friend functions 
    // friend types 
    // private member functions 
    // ... 
}; 

知道的約定,可以快速查看,而無需檢查整個類聲明~MyClass()是隱式默認的,並且附近有數據成員,很容易看到編譯器聲明和提供的析構函數做了什麼。

接下來我們可以看到MyClass有一個顯式默認的默認構造函數,並且附近聲明瞭數據成員,很容易看出編譯器提供的默認構造函數的作用。這也很容易看到爲什麼默認的構造函數已經明確宣佈:因爲我們需要一個用戶定義的拷貝構造函數,這將抑制編譯器提供的默認構造函數,如果沒有明確違約。

接下來,我們看到有一個用戶提供的拷貝構造函數和拷貝賦值運算符。爲什麼?那麼,通過附近的數據成員,很容易推測可能需要unique_ptr ptr_的深層副本。當然,如果不檢查複製成員的定義,我們當然無法知道。但即使沒有這些定義,我們已經很瞭解。

對於用戶聲明的副本成員,如果我們什麼都不做,則移動成員將隱含地不聲明。但在這裏我們很容易看到(因爲所有內容都可預測地分組並在MyClass聲明的頂部排序),因此我們明確地默認了移動成員。再次,因爲數據成員在附近,我們可以立即查看這些由編譯器提供的移動成員將執行的操作。

總之,我們還沒有確切的瞭解MyClass的功能以及它在這個程序中扮演的角色。然而,即使缺乏這方面的知識,我們已經對MyClass知道了很多。

我們知道MyClass

  • 持有一個唯一擁有指向一些(可能是多態)OtherClass
  • 保存作爲名稱的字符串。
  • 也有不少雙打切斷爲某種數據。
  • 會妥善破壞自己而不會泄漏任何東西。
  • 默認構造自己的結果爲空ptr_,空name_data_
  • 會自我複製,而不是正確的如何,但有一個可能的算法,我們可以很容易地檢查其他地方。
  • 將通過移動三個數據成員中的每一個來高效(正確地)移動自己。

在10行左右的代碼中需要了解很多。而且我們不必通過數百行代碼進行搜索,我肯定需要正確實施MyClass以瞭解所有這些內容:因爲它全部位於頂部並且處於可預測的順序。

有人可能想調整這個配方,說在數據成員之前放置嵌套類型,以便可以根據嵌套類型聲明數據成員。然而,這項建議的精神在於宣佈私人數據成員和特殊成員,儘可能地接近頂端,並儘可能接近彼此。這與過去(甚至可能是我自己)給出的建議相反,私人數據成員是一個實現細節,不足以在類聲明的頂部。

但在事後(事後總是20/20),私有數據成員,即使是無法進入到遙遠的代碼(這是一件好事)聽寫和描述一個類型的基本行爲時,其任何特殊成員是編譯器提供的。 知道一個班的特殊成員做什麼,是理解任何類型的最重要的方面之一。

  • 它是否可破壞?
  • 它是默認可構造的嗎?
  • 可複製嗎?
  • 可移動嗎?
  • 它有價值的語義或引用語義嗎?

每個類型都有這些問題的答案,並且最好是得到這些問題的答案&出盡快的方式。那麼你可以更容易地專注於使這種類型與其他類型不同的東西。

+1

我一直在試驗你的建議,我非常喜歡它!具有數據成員的頂部部分似乎也是將小型私有函數用於檢查前置條件和後置條件以及不變量的自然之處,因爲這對於類提供的抽象類型也非常有用。 – TemplateRex 2014-12-02 09:38:04

1

當您試圖維護rule of 5以確保特殊成員函數按照您的意願行事時,您經常會看到= default,因此,班級的讀者可以看到您確實考慮了您希望該功能的行爲方式。

例如,如果您打算使某些內容不可複製或不可移動,則可以使用= delete。雖然我也看到人們如果不希望特定的派生類具有該功能,那麼他們就是一個繼承的函數,儘管我並不是那麼喜歡它,因爲它往往指向糟糕的體系結構/設計。

+0

默認情況下,我可以正確地使用它,但是您認爲通常看到某些內容是不可複製的,但是可以移動嗎?或相反亦然?如果你知道哪裏可以指向我,我會很樂意看到一些例子! :) – flakes 2014-12-01 16:53:42

+2

@Calpratt:std :: unique_ptr是可移動的,但不可複製。 – erenon 2014-12-01 17:05:50

+0

@Calpratt大部分價值類都是可複製的,但不可移動。你通常只會費心去做一些可移動的事情1)如果你需要特殊的傳輸語義(在這種情況下,它是不可複製的),或者2)分析器顯示覆制是應用程序中的一個瓶頸。 – 2014-12-01 17:46:09

4

此外,使用=default代替手卷一個保持類的POD性質,因爲它是在這裏詳細描述:Default constructors and POD

+1

這實際上是關於'= default'的正確答案。它幾乎總是被使用,因爲你想要默認行爲(可能包括POD-ness),但不希望函數是「public」。 – 2014-12-01 17:47:38