2016-12-22 26 views
4

下面的代碼編譯,但在運行時提供了一個錯誤的連續內存塊:析構函數是無法刪除已分配

# include <iostream> 
# include <string.h> 

class A { 
public: 
    A() {} 
    A (int id, char * t_name) { 
    _id = id ; 
    name = new char [ strlen (t_name) + 1 ] ; 
    strcpy (name, t_name) ; 
    } 

    char *name ; 
    int _id ; 
    ~A() { delete [] name ;} 
} ; 

int main() { 
    A a (1, "123") ; 
    A b ; 
    b = a ; 

    std::cout << static_cast < const void * > (a.name) << std::endl ; 
    std::cout << static_cast < const void * > (b.name) << std::endl ; 

    b.name = "abc" ; // b.name is directed to a different memory block 
    std::cout << static_cast < const void * > (a.name) << std::endl ; 
    std::cout << static_cast < const void * > (b.name) << std::endl ; 
    std::cout << a.name << std::endl ; 
    std::cout << b.name << std::endl ; 

    return 0 ; 
} 

它輸出類似:

0x7ff87bc03200 
0x7ff87bc03200 
0x7ff87bc03200 
0x10f9bcf64 
123 
abc 
a.out(883,0x7fff7ee3d000) malloc: *** error for object 0x10f9bcf64: 
pointer being freed was not allocated 
*** set a breakpoint in malloc_error_break to debug 
Abort trap: 6 

我不明白它爲什麼說:

0x10f9bcf64:被釋放的指針未被分配

因爲b.name顯然是針對0x10f9bcf64,並且不再與a重疊!

我也想知道如何解決這個問題?謝謝 !

+4

規則的精彩世界你已經違反了法則三。 –

+1

'b.name'不是'new''d內存,所以它不能'delete'd –

回答

1

對於初學者的構造函數聲明應該像

A (int id, const char * t_name) 
      ^^^^^^ 

,因爲你使用字符串文字來初始化類和字符串文字的對象有類型的常量數組。

默認的複製賦值操作符使對象的數據成員的成員智能副本。相對於此語句後的代碼,

b = a ; 

對象將具有兩個指向相同動態分配內存的指針。因此delete操作符將被調用兩次以獲得相同的內存地址。

你必須明確地寫出你的類的複製賦值操作符和複製構造函數。

例如,拷貝賦值運算符可以看看下面的方式

A & operator = (const A &a) 
{ 
    if (this != &a) 
    { 
     char *tmp = new char[ std::strlen(a.name) + 1 ]; 
     std::strcpy(tmp, a.name); 

     delete [] this->name; 

     this->_id = a._id; 
     this->name = tmp; 
    } 

    return *this; 
} 

本聲明

​​

也是錯誤的。字符串文字具有靜態存儲持續時間。所以你可能不會刪除他們的記憶。

+0

不僅需要賦值運算符,還要複製構造函數和移動操作(參考3/5/0的規則),除非使用'std :: string'來刪除手動管理內存的想法。此外,錯誤不是由'b = a;'引起的,而是由'b.name =「abc」;'(但沒有'b.name =「abc」;','b = a;'確實會導致錯誤) – wasthishelpful

+0

@wasthishelpful首先你完全錯了。錯誤的原因是沒有複製賦值操作符。其次,我在答覆中指出,複製構造函數必須明確定義。第三,不需要爲這樣簡單的程序定義移動特殊功能。 –

+0

我的歉意,你確實提到了拷貝構造函數^^我們不知道OP背後的真實用法,所以恕我直言,我們不能說沒有必要定義移動特殊函數。我們在這裏提到他們,我很滿意^^我不同意你的看法:OP代碼中的已刪除內存是**而不是**從'a'複製的指針。如果沒有重新分配「b.name」,那麼我會同意你關於OP中錯誤的原因。除OP之外,缺失的複製賦值操作符確實是一個錯誤,所以我同意你的意見:) – wasthishelpful

0

您正在將指針從實例a複製到實例b。

實例a的析構函數運行時,會刪除內存。

當實例b的析構函數運行時,它會再次刪除相同的內存。

您需要爲此類添加副本和賦值構造函數。 (和移動的構造函數,如果你正在使用C++ 11)

0

我不明白爲什麼它說:「0x10f9bcf64:指針被釋放了 未分配」,因爲b.name顯然是針對0x10f9bcf64和 不再與a的重疊!

因爲b的析構函數在靜態字符串上調用delete []

我也想知道如何解決這個問題。

你應該定義一個拷貝構造函數,喜歡的東西:

A::A(const A& lhs) { 
    _id = lhs.id; 
    name = new char [ strlen (lhs.name) + 1 ] ; 
    strcpy (name, lhs.name) ; 
} 

而且也取得了name_idprivate

3

您應該先閱讀約Rule of 3/5/0。您的發言:

b = a; 

是違反3規則(5如果你使用現代C++,即C++ 11或更高版本),因爲你的A類有一個指針作爲成員。

接下來,如果你認爲這樣的說法:

b.name = "abc"; 

你在這裏影響靜態字符數組沒有用new分配。所以,當你的析構函數試圖將其刪除:

~A() { delete [] name ;} 

delete[]調用生成您的錯誤。

一個簡單的解決辦法是宣佈namestd::string

class A { 
public: 
    A() {} 
    A (int id, const std::string& t_name) { 
    _id = id ; 
    name = t_name; 
    } 

    std::string name ; 
    int _id ; 
} ; 

int main() { 
    A a (1, "123") ; 
    A b ; 
    b = a ; 

    std::cout << static_cast < const void * > (&a.name) << std::endl ; 
    std::cout << static_cast < const void * > (&b.name) << std::endl ; 

    b.name = "abc" ; // b.name is directed to a different memory block 
    std::cout << static_cast < const void * > (&a.name) << std::endl ; 
    std::cout << static_cast < const void * > (&b.name) << std::endl ; 
    std::cout << a.name << std::endl ; 
    std::cout << b.name << std::endl ; 

    return 0 ; 
} 

由於std::string爲你管理內存,你回0