2012-01-23 48 views
4

這裏是我的C++代碼:由於對象是按值傳遞給SomeFunc的析構函數析構函數在C++中是如何工作的

Say i am in someFunc 
Null pointer assignment(Run-time error) 

這裏:

class Sample 
    { 
    public: 
      int *ptr; 
      Sample(int i) 
      { 
      ptr = new int(i); 
      } 
      ~Sample() 
      { 
      delete ptr; 
      } 
      void PrintVal() 
      { 
      cout << "The value is " << *ptr; 
      } 
    }; 
    void SomeFunc(Sample x) 
    { 
    cout << "Say i am in someFunc " << endl; 
    } 
    int main() 
    { 
    Sample s1= 10; 
    SomeFunc(s1); 
    s1.PrintVal(); 
    } 

它返回我像輸出當控件從函數返回時調用對象

我應該對嗎?如果是,那麼爲什麼會發生?什麼是這個解決方案?

+1

參見:[三規則(http://stackoverflow.com/questions/4172722/what-is-the-rule -of-three) –

+1

編譯器正在爲您構建一個默認的複製構造函數,無論何時在您的類中分配內存(以及賦值運算符)時,都需要重寫 - 請參閱下面的Rule of Three鏈接 – kfmfe04

回答

4

Sample通過值傳遞到SomeFunc,這意味着一個副本。該副本具有相同的ptr,因此當該副本在SomeFunc返回時被銷燬時,將爲這兩個對象刪除ptr。然後,當您在主要中調用PrintVal()時,您將取消引用此無效指針。這是未定義的行爲。即使這樣做,那麼當s1被破壞ptr被再次刪除,這也是UB。

此外,如果編譯器無法取消Sample s1= 10;中的副本,那麼s1甚至不會有效,因爲當臨時被銷燬時指針將被刪除。儘管大多數編譯器都會避免這個副本。

您需要正確複製或不允許複製。這種類型的默認拷貝不正確。我建議將這種類型的值類型(它直接保存它的成員而不是指針),以便默認的copy-ctor工作,或者使用智能指針來保存引用,以便它可以管理引用資源你和默認的copy-ctor仍然可以工作。

我真正喜歡C++的一件事情是,它非常友好地使用各種值類型,並且如果您需要引用類型,則可以將任何值類型包裝到智能指針中。我認爲這比其他具有基本類型和值語義的語言好得多,但默認情況下用戶定義的類型具有引用語義。

+0

可能值得補充的是'Sample s1 = 10;'也可以調用複製構造函數。 –

+0

@JamesKanze謝謝,我會補充一點。 – bames53

+0

@Milracle:你可以閱讀更多關於[[三規則]](http://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming))。 –

5

由於您有指針成員,因此您通常需要服從Rule of Three
在你的代碼示例,以避免未定義行爲,你所看到的:

通過必須更換需要在第一個語句。

+0

不需要服從,如果你知道你在做什麼。稍微不太強迫的措辭可能會很好。 – pmr

+0

@pmr:我希望這樣做不夠強迫。 –

+0

@pmr:這可能足以構成一個新問題,但在哪些情況下你會不遵守三項規則,因爲你知道你在做什麼?或者你只是在談論你讓其他成員爲私人並因此無法訪問的情況? –

0

當你傳遞函數的參數時,它被稱爲拷貝構造函數,但是你沒有一個,所以指針沒有被初始化。當它退出函數時,該對象調用析構函數來刪除單位化指針,因此會出錯。

+0

他有一個複製構造函數,否則代碼將不會編譯。這是隱含生成的,這就是問題所在。 –

+0

指針不是未初始化的,它用一個副本初始化,導致兩個破壞。 – PlasmaHH

1

由於SomeFunc()按值取其參數,因此您傳遞給它的Sample對象將被複制。當SomeFunc()返回時,臨時副本被銷燬。

由於Sample沒有定義拷貝構造,其編譯器生成的複製構造簡單地拷貝該指針值,所以既Sample實例指向同int。當一個Sample(臨時副本)被銷燬時,該int被刪除,然後當第二個Sample(原件)被銷燬時,它會嘗試再次刪除相同的int。這就是爲什麼你的程序崩潰。

您可以更改SomeFunc()採取引用,而不是,避免了臨時副本:

void someFunc(Sample const &x) 

和/或可以定義一個拷貝構造函數用於Sample它分配一個新的int,而不僅僅是複製指針現有的。

0

而不是

int main() 
{ 
Sample s1= 10; 
SomeFunc(s1); 
s1.PrintVal(); 
} 

嘗試使用

int main() 
{ 
Sample* s1= new Sample(10); 
SomeFunc(*s1); 
s1->PrintVal(); 
}