2016-06-26 64 views
-4

以下代碼片段具有用戶定義的數據類型String。該類的對象存儲字符指針str(字符串的簡寫)和length在C++中調用析構函數

#include<iostream> 
#include<cstring> 
using namespace std; 
class String 
{ 
    char* str; 
    int length; 
public: 
    String(){}      //default constructor 
    String(const char* s) 
    { 
     length=strlen(s); 
     str=new char[length+1]; 
     strcpy(str,s); 
    } 
    void add(String a,String b) //function to concatenate strings 
    { 
     length=a.length+b.length; 
     str=new char[length+1]; 
     strcpy(str,a.str); 
     strcat(str,b.str); 
    } 
    void display() 
    { 
     cout<<str<<endl; 
    } 
    ~String()        //destructor 
    { 
     delete str; 
     cout<<"Destructor invoked!"; 
    } 
}; 
int main() 
{ 
    String s1; 
    String s2("Well done!"); 
    String s3("Boy"); 
    s1.add(s2,s3); 
    s1.display(); 
    s2.display(); 
    s3.display(); 
} 

輸出:

Destructor invoked!Destructor invoked!Well done!boy 
X!!; 
<!!; 
Destructor invoked!Destructor invoked!Destructor invoked! 
  • 看來好像析構函數的 display功能之前,甚至調用得到invoked.Why是這樣嗎?

如果不定義析構函數,我得到下面的輸出(如預期):

Well done!boy 
Well done! 
boy 
  • 爲什麼在定義一個析構函數這個意外的輸出?
+3

析構函數被調用是因爲'add'的參數被銷燬。 – MikeCAT

+4

你的班級不符合3級規則。您需要至少添加複製構造函數和複製賦值運算符。 –

+0

你有兩個內存泄漏*和*雙重刪除。看看'add',和[三是什麼規則?](http://stackoverflow.com/questions/4172722/what-is-the-rule-of-ree)。 – juanchopanza

回答

1

這個默認的構造函數

String(){} 

不初始化任何東西,因爲這兩個數據成員是基本類型和基本類型,不提供自動初始化。

結果實例的任何用法將因此使用(數據成員的)不確定值,因此將具有未定義的行爲。

修復:初始化數據成員。


您還需要負責複製和移動,例如,避免雙重delete s –這也是未定義的行爲。

示例代碼副本在呼叫

s1.add(s2,s3); 

&hellip String實例;因爲add通過值獲取參數。

修復:定義至少一個拷貝構造函數。


在析構的解除分配

delete str; 

&hellip;與使用new[]str的分配不匹配。

它可以在實踐中工作,但它是未定義的行爲,因此可能會導致各種問題。

修復:使用delete[]new[]創建的東西。


固定的代碼也會泄漏存儲器上述過程之後。一個簡單的修復可能是定義一個連接的構造函數,讓add使用它來創建一個新的String實例,然後使用swap該實例的狀態和當前實例。這種方法具有自動(很好,幾乎自動)異常安全的優點。


一般建議:

  • 相反的<cstring>,只需使用<string.h>。一個優點是,像同名的C頭一樣,它保證將名稱放在全局名稱空間中。所以如果你使用像strcpy這樣的非限定名字,它將可移植工作,不僅僅是手頭編譯器的出現。

  • 代替通過值傳遞潛在的大的物體,如在void add(String a,String b)的,通過引用傳遞他們const,如在void add(String const& a,String const& b),以避免不必要的複製。

  • 優先不要在不關心I/O的類中執行I/O,如void display()成員函數。例如,display()功能在圖形用戶界面中不起作用。

1

你需要拷貝構造爲這樣:void add(String a,String b); 這是更好通過引用傳遞字符串:

void add(const String &a, const String &b); 

而且在析構函數需要釋放用delete []內存。

delete[] str; 

現在它會工作。

+0

Re「現在它會工作。」,不,該代碼在添加複製構造函數後仍然具有UB。 –