2012-02-27 18 views
56

在C++ 11中顯式刪除成員函數,是否仍然值得從不可複製的基類繼承?在C++ 11中顯式刪除成員函數,是否仍然值得從不可複製的基類繼承?

我在談論你私下繼承一個具有私有或刪除拷貝構造函數和拷貝分配的基類的技巧(例如boost::noncopyable)。

在這個question中提出的優點仍然適用於C++ 11嗎?


我不明白爲什麼有些人聲稱在C++ 11中製作一個不可複製的類更容易。

在C++ 03:

private: 
    MyClass(const MyClass&) {} 
    MyClass& operator=(const MyClass&) {} 

在C++ 11:

MyClass(const MyClass&) = delete; 
MyClass& operator=(const MyClass&) = delete; 

編輯:

正如許多人所指出的,這是爲私人拷貝構造函數和拷貝賦值操作符be提供空體(即{})時出錯因爲這會允許類本身無誤地調用這些運算符。我首先開始不添加{},但遇到了一些鏈接器問題,這些問題讓我添加了一些愚蠢的理由(我不記得這些情況)。我知道更好知道。 :-)

+2

你不是在描述一個習慣用法,而只是一個實現。這個習語仍然存在,現在寫起來更簡單。 – 2012-02-27 00:42:16

+0

它比編寫一個空的私人拷貝構造函數和私人拷貝賦值操作符更簡單嗎? – 2012-02-27 00:45:07

+0

編輯避免濫用術語「成語」。 – 2012-02-27 00:47:58

回答

73

那麼,這:

private: 
    MyClass(const MyClass&) {} 
    MyClass& operator=(const MyClass&) {} 

技術上還允許MyClass由成員和朋友被複制。當然,這些類型和功能在理論上是在你的控制之下,但該類仍然是可複製的。至少用boost::noncopyable= delete,沒有人可以複製這個類。


我不明白爲什麼有些人聲稱這是容易使一類不可複製的C++ 11。

它不像「更容易消化」那麼「容易」。

考慮一下:

class MyClass 
{ 
private: 
    MyClass(const MyClass&) {} 
    MyClass& operator=(const MyClass&) {} 
}; 

如果你是一個C++程序員誰已經閱讀C++的介紹文字,但很少接觸地道的C++(即:一個很多C++程序員),這是混亂。它聲明瞭拷貝構造函數和拷貝賦值操作符,但它們是空的。那麼爲什麼要申報呢?是的,他們是private,但只提高更多問題:爲什麼讓他們私密?

要理解爲什麼這會阻止複製,你必須認識到,通過聲明它們是私人的,你可以讓非成員/朋友不能複製它。這對新手來說並不明顯。當他們試圖複製它們時,也不會得到錯誤信息。

現在,將其比作C++ 11版:

class MyClass 
{ 
public: 
    MyClass(const MyClass&) = delete; 
    MyClass& operator=(const MyClass&) = delete; 
}; 

怎樣才能明白,這個類不能被複制?無非是瞭解= delete語法的含義。任何解釋C++ 11語法規則的書都會告訴你具體做了什麼。對於沒有經驗的C++用戶來說,這段代碼的效果是顯而易見的。

這個習語很棒,它會成爲一個成語,因爲它是最明確,最明顯的表達方式。

即使boost::noncopyable需要更多的思考。是的,它被稱爲「不可複製」,所以它是自我記錄。但是,如果你以前從未見過它,它會引發問題。你爲什麼從某些不能複製的東西中獲得?爲什麼我的錯誤消息談論boost::noncopyable的拷貝構造函數?等等。再次,理解這個習語需要更多的精力。

+12

+1我喜歡你如何說明'= delete'不一定更容易編寫,但更易讀*。 – 2012-02-27 01:11:18

+1

我只想指出,沒有定義身體的私人ctor和複製賦值操作符會做伎倆。我們可以聲明'MyClass(const MyClass&);'而不是提供空的定義'MyClass(const MyClass&){}'並且這種方法在它自己內部是不可複製構造的。然而'= delete'使程序員的意圖清晰並且不言自明。 – doc 2015-09-03 14:16:30

5

它更具可讀性,並允許編譯器提供更好的錯誤。

'已刪除'對讀者來說更加清晰,特別是如果他們正在瀏覽課程。同樣,編譯器可以告訴你,你試圖複製一個不可複製的類型,而不是給你一個通用的「試圖訪問私有成員」的錯誤。

但實際上,這只是一個便利功能。

25

的第一件事就是爲別人在我之前指出的,你得到了成語錯了,你聲明爲私有,不定義

class noncopyable { 
    noncopyable(noncopyable const &); 
    noncopyable& operator=(noncopyable const &); 
}; 

operator=的返回類型基本上什麼都可以。此時,如果您在標題中閱讀該代碼,它實際上意味着什麼?它只能由朋友複製,否則無法複製?請注意,如果您提供的定義與您的示例中相同,則表示我只能在課堂內複製,而朋友,因此缺少將其轉換爲的定義。我無法複製。但是在頭文件中缺少定義並不是因爲它可以在cpp文件中定義,所以無處不在的定義的同義詞

這是由一種被稱爲繼承不可複製使得它明確這樣做的目的是爲了避免拷貝,以同樣的方式,如果您手動寫上面的代碼,你應該文件與該行的評價是意圖是禁用副本。

C++ 11不會更改任何這些,它只是使代碼中的文檔顯式化。在你聲明拷貝構造函數被刪除的同一行中,你被記錄爲你想要它完全禁用

作爲最後的評論,在C++ 11的功能不只是能夠編寫不可複製用更少的代碼或更好的,而是關於抑制從生成你不想生成的代碼編譯器。這只是該功能的一種用法。

+3

**你提出了一個非常重要的觀點**:空的私有實現並不妨礙你錯誤地複製對象!這個習語不*提供空的私人定義。這個習慣用法是隻提供*聲明*。 – 2013-10-07 21:07:22

+1

@Kuba此解決方案不適用於符合標準的編譯器/鏈接器,因爲它們要求在聲明成員函數時還必須定義它。例如,即使沒有代碼使用它們,Green Hills編譯器/鏈接器也會失敗,並顯示鏈接器錯誤,指出缺少關於複製ctor和賦值運算符的定義。 – ThreeBit 2014-01-07 23:00:06

+1

這很有趣。我認爲這樣的編譯器/鏈接器組合會被打破,因爲有很多代碼使用這個習語而沒有提供定義。想起整個Qt工具包。 – 2014-01-07 23:03:16

10

除了別人帶來了點...

擁有一個私人拷貝構造函數和拷貝賦值運算符,你不從製作副本定義阻止任何人。但是,如果成員函數或朋友函數試圖進行復制,他們將收到鏈接時錯誤。如果他們試圖在明確刪除這些函數的地方這樣做,他們將收到編譯時錯誤。

我總是讓我的錯誤儘快發生。使運行時錯誤發生在錯誤點而不是稍後(因此,當您更改變量而不是讀取它時,會發生錯誤)。將所有運行時錯誤都變爲鏈接時錯誤,以便代碼永遠不會有錯。將所有鏈接時錯誤編譯爲編譯時錯誤,以加快開發速度並獲得稍微有用的錯誤消息。

2

這裏的人建議您聲明成員函數而不定義它們。我想指出,這種方法是不便攜的。有些編譯器/連接器要求,如果你聲明瞭一個成員函數,那麼你也必須定義它,即使它沒有被使用。如果你只使用VC++,GCC,clang,那麼你可以避開這個,但是如果你正在嘗試編寫真正可移植的代碼,那麼其他一些編譯器(例如Green Hills)將會失敗。

+1

我對此答案持懷疑態度。因爲在多個編譯單元中定義一個類的成員函數是合法的,所以實現這個反特性實際上很困難。使用gcc/clang/icc的例子? – Praxeolitic 2014-06-18 07:28:26

相關問題