2017-03-01 151 views
3

下面的代碼編譯:爲什麼用戶定義的析構函數的插入需要一個用戶定義的複製構造

#include <vector> 
#include <iostream> 
#include <memory> 

using namespace std; 

class container 
{ 
public: 
    container(){} 
    ~container(){} 
}; 

class Ship 
{ 
public: 
    Ship(){} 
    //Ship(const Ship & other){cout<<"COPY"<<endl;} 
    //~Ship(){} 

    std::unique_ptr<container> up; 
}; 

Ship buildShip() 
{ 
    Ship tmp; 
    return tmp; 
} 

int main(int argc, char *argv[]) 
{ 
    return 0; 
} 

但是,如果我們有定義的析構函數~Ship(){}用戶,代碼將只編譯如果我們還包括用戶定義的複製構造Ship(const Ship & other){cout<<"COPY"<<endl;}

簡而言之:

編譯:

Ship(){} 
//Ship(const Ship & other){cout<<"COPY"<<endl;} 
//~Ship(){} 

編譯:

Ship(){} 
Ship(const Ship & other){cout<<"COPY"<<endl;} 
~Ship(){} 

不會編譯:

Ship(){} 
//Ship(const Ship & other){cout<<"COPY"<<endl;} 
~Ship(){} 

爲什麼用戶定義的析構函數的插入需要一個用戶自定義拷貝構造函數和爲什麼我們需要在上面的例子中拷貝構造函數所有?

我希望在上面的例子中不需要複製構造函數,因爲unique_ptr甚至不能被複制。

+0

https://godbolt.org/g/rFoUWi - 鐺3.9.1在這裏所做的行爲這樣 – Ap31

+0

還鏗鏘: 錯誤(S): source_file.cpp:27:12:錯誤:調用隱含缺失 '船舶' 的拷貝構造函數 返回TMP; ... – newandlost

+0

是,所有4個編譯器說副本定義析構函數時,構造函數會被隱式刪除 – Ap31

回答

11

的代碼被編譯因爲buildShip()會在返回tmp時使用編譯器自動生成的移動構造函數。添加用戶聲明的析構函數可防止編譯器自動生成一個析構函數。例如,參見thisthis問題。由於成員upstd::unique_ptr,因此編譯器生成的拷貝構造函數無法使用。明確刪除unique_ptr的複製構造函數。

因此,這將編譯,因爲編譯器被明確要求產生的移動構造函數:

class Ship 
{ 
public: 
    Ship(){} 
    Ship(Ship&&) = default; 
    ~Ship(){} 
    std::unique_ptr<container> up; 
}; 
+0

只是爲了確保我理解它:包括用戶定義的析構函數可防止編譯器自動生成移動運算符。由於移動操作符丟失,buildShip函數的返回嘗試複製tmp Ship,從而調用默認的拷貝構造函數,然後嘗試複製tmp,從而導致產生錯誤的unique_ptr。對? – newandlost

+0

yes,line'return tmp;'試圖使用由'unique_ptr'成員明確刪除的'Ship'副本。 – AMA

+0

再次感謝@AMA很好的回答!就像你已經提供了我以前的[問題](http://stackoverflow.com/questions/42491544/c-how-to-avoid-access-of-members-of-a-object-that-was-not-yet -initialized)。 – newandlost

0

的想法是,如果編譯器生成的析構函數是不是你的類足夠好,那麼很有可能是拷貝構造函數和拷貝賦值運算符也不夠好,所以編譯器可以刪除的隱含實現那些複製操作。從技術上講,即使你有一個用戶定義的析構函數,編譯器仍然可以給你隱式拷貝操作,但是這種行爲在C++ 11中已經被棄用了。

Rule of Three

據我所知,你仍然需要一個拷貝構造函數,因爲值buildShip()回報。

(然而,有趣的是,你可以編譯使用隱式的拷貝構造函數的版本,你不應該是能夠做到這一點,因爲unique_ptr成員...的)

+0

Re your afaik:buildShip返回值並不意味着需要顯式拷貝構造函數。這不是特例,與其他任何事情都是一樣的,隱含的可能是好的,也可能不是,但這不取決於buildShip。它使用* a *拷貝構造函數,但不一定需要* explicit *。 –

相關問題