2009-04-27 90 views
4

我寫了一個包含自定義構造函數,析構函數,複製構造函數和賦值運算符的樣本類的小測試程序。我很驚訝,當我意識到拷貝構造函數是不是在所有調用,即使我實現我的同班同學的返回值功能和線條狀Object o1; Object o2(o1);如何強制編譯器使用顯式拷貝構造函數?

innerclass.hpp:

#include <iostream> 

class OuterClass 
{ 
public: 
OuterClass() 
{ 
    std::cout << "OuterClass Constructor" << std::endl; 
} 
~OuterClass() 
{ 
    std::cout << "OuterClass Destructor" << std::endl; 
} 
OuterClass(const OuterClass & rhs) 
{ 
    std::cout << "OuterClass Copy" << std::endl; 
} 
OuterClass & operator=(const OuterClass & rhs) 
{ 
    std::cout << "OuterClass Assignment" << std::endl; 
} 

class InnerClass 
{ 
public: 
    InnerClass() : m_int(0) 
    { 
     std::cout << "InnerClass Constructor" << std::endl; 
    } 
    InnerClass(const InnerClass & rhs) : m_int(rhs.m_int) 
    { 
     std::cout << "InnerClass Copy" << std::endl; 
    } 
    InnerClass & operator=(const InnerClass & rhs) 
    { 
     std::cout << "InnerClass Assignment" << std::endl; 
     m_int = rhs.m_int; 
     return *this; 
    } 
    ~InnerClass() 
    { 
     std::cout << "InnerClass Destructor" << std::endl; 
    } 
    void sayHello() 
    { 
     std::cout << "Hello!" << std::endl; 
    } 

private: 
    int m_int; 
}; 

InnerClass innerClass() 
{ 
    InnerClass ic; 
    std::cout << "innerClass() method" << std::endl; 
    return ic; 
} 
}; 

innerclass.cpp:

#include "innerclass.hpp" 

int main(void) 
{ 
std::cout << std::endl << "1st try:" << std::endl; 


OuterClass oc; 
OuterClass oc2(oc); 
oc.innerClass().sayHello(); 

std::cout << std::endl << "2nd try:" << std::endl; 

OuterClass::InnerClass ic(oc.innerClass()); 
ic = oc.innerClass(); 
} 

輸出:

1st try: 
OuterClass Constructor 
OuterClass Copy 
InnerClass Constructor 
innerClass() method 
Hello! 
InnerClass Destructor 

2nd try: 
InnerClass Constructor 
innerClass() method 
InnerClass Constructor 
innerClass() method 
InnerClass Assignment 
InnerClass Destructor 
InnerClass Destructor 
OuterClass Destructor 
OuterClass Destructor 

甲我讀過一些研究,並不保證編譯器會使用明確定義的拷貝構造函數。我不明白這種行爲。爲什麼複製構造函數甚至存在,如果我們不知道它被調用?編譯器如何決定它是否使用它?

或者,更好的辦法是強制編譯器使用自定義的拷貝構造函數嗎?

+0

您可以鏈接到你在讀什麼?我懷疑你誤解了一些東西。 – 2009-04-27 13:28:27

+0

發佈一些代碼。我們需要看看你是如何定義拷貝構造函數的。 – 2009-04-27 13:29:05

+0

確定o1沒有被隱式轉換爲Object可構造的其他類型?沒有特定的代碼,沒有真正的方法來說明爲什麼你的代碼會失敗。 – 2009-04-27 13:31:07

回答

6

只是與其他答案的完整性,該標準允許編譯器省略副本在某些情況下構造函數(其他答案稱爲「返回值優化」或「命名返回值優化」 - RVO/NRVO):

12.8複製類對象,第15段(C++ 98)

每當臨時類對象是使用一個拷貝構造複製,並且該對象和副本具有相同的CV-非限定類型,一種實施方式是即使類拷貝構造函數或析構函數具有副作用,也允許將原始拷貝和副本視爲引用同一對象的兩種不同方式,並且根本不執行拷貝。對於具有類返回類型的函數,如果return語句中的表達式是本地對象的名稱,並且本地對象的cv-unqualified類型與函數返回類型相同,則允許實現省略創建臨時對象保存函數返回值,即使類拷貝構造函數或析構函數具有副作用。在這些情況下,該對象在原始和副本未經優化而被銷燬的時間晚些時候被銷燬。

因此,在你innerClass()方法,你可能會想拷貝構造函數將在返回稱爲允許被優化掉:

InnerClass innerClass() { 
    InnerClass ic; 
    std::cout << "innerClass() method" << std::endl; 
    return ic; // this might not call copy ctor 
} 
4

您不應該在特定情況下依賴於被調用(或未調用)的拷貝構造函數來設計類。編譯器被允許在各種地方刪除或添加拷貝構造函數調用,而且你真的不想跟蹤它們。至於爲什麼你需要一個 - 編譯器可能會決定它需要一個副本,而拷貝構造函數就是它用來做這件事的。複製構造函數調用被忽略的好處是性能 - 複製通常是相當昂貴的操作。

+0

我是否理解您的權利......複製構造函數可能會被調用,也可能不會。但是*如果需要*,那麼它總是毫無例外地是明確定義的,而不是* *由編譯器自動生成的任何東西。 – Chris 2009-04-27 13:56:00

+0

這是正確的。 – 2009-04-27 14:00:27

+1

換句話說,不要在你的拷貝構造函數中放置副作用,因爲編譯器在某些情況下有迴避它們的機會 – 2009-04-27 17:39:41

0

Object o1();不會創建任何對象,而是定義一個函數原型,函數名稱爲o1,void參數和返回類型爲Object。你需要發佈一些代碼來查找實際問題。

4

我同意尼爾,你不應該寫一個依賴於被調用的拷貝構造函數的類。這是因爲編譯器可以執行諸如「命名返回值優化」(link)之類的操作,它完全避免了許多返回值場景中的複製構造函數。爲了強制調用複製構造函數,您需要編寫大量旨在「欺騙」C++編譯器的代碼。不是一個好主意。

在特定情況下,如果您想強制調用複製構造函數,您可以進行顯式調用。

Object SomeFunc() { 
    Object o1 = ... 
    return Object(o1); 
} 
0

發表一些代碼。我認爲你正在使用一些不正確的語法。

拷貝構造函數必須完全具備以下特徵:

MyObject(const MyObject&) 

然後,你可以看到,如果拷貝構造函數被調用下面的代碼:

MyObject m1; 
MyObject m2(m1); 

您不允許使用MyObject m1();這是一個函數聲明。

2

這是問題嗎?

OuterClass(const OuterClass & rhs) 
{   
std::cout << "OuterClass Constructor" << std::endl; 
==> 
std::cout << "OuterClass Copy Constructor" << std::endl; 

} 

OuterClass & operator=(const OuterClass & rhs) 
{  
std::cout << "OuterClass Constructor" << std::endl; 
==> 
std::cout << "OuterClass Assignment operator" << std::endl; 
} 

複製粘貼錯誤!

我建議你調試一次代碼,看看究竟發生了什麼。調試確實可以幫助您找到問題。

編輯:的內部類問題:

正如其他人已經指出這是的情況下,名稱返回值優化(NRVO)。

InnerClass innerClass() 
{  
    InnerClass ic;   
    std::cout << "innerClass() method" << std::endl;   
    return ic; 
} 

編譯器可以轉換的功能

void innerClass(InnerClass &namedResult) 

{ 
std::cout << "innerClass() method" << std::endl; 

} 

因此,同時消除由類對象,並需要調用類的拷貝構造函數的返回值。

請通過以下兩個鏈接,瞭解更多關於NRVO: