2016-11-22 27 views
-2

我試圖在析構函數中使用delete[]來刪除已創建的指針。當使用delete[] string;時,我收到了垃圾。沒有delete,我的代碼出來正確的:因爲你沒有正確遵循了Rule of Three動態內存和類

class test 
{ 
public: 
    test operator=(char* tests) 
    { 
     this->string = tests; 
     return *this; 
    } 

    int starlen() 
    { 
     i = 0; 
     while (this->string[i] != '\0') 
      i++; 

     return i; 
    } 

    test operator+(test& tests) 
    { 
     a = strlen(this->string); 
     b = strlen(tests.string); 

     ptr = this->string; 
     ptr2 = tests.string; 

     for (int i = a; i < a + b; i++) 
     { 
      *(ptr + i) = *(ptr2 + i - a); 
     } 

     ptr[a + b] = '\0'; 
     return ptr; 
    } 

    void printit() 
    { 
     cout << string; 
    } 

    test() 
    { 
     string = 0; 
    } 

    test(char* ptr) 
    { 
     string = new char[10]; 
     strcpy(string, ptr); 
    } 

    ~test() 
    { 
     //delete[] string; ???? 
    } 

public: 
    char* string; 
    int a; 
    int b; 
    char* ptr; 
    char* ptr2; 
    int i; 
}; 

int main() 
{ 
    test t1("book"); 
    test t2("shelf"); 
    test t3; 
    t3 = t1 + t2; 
    cout << t3.starlen() << endl; 

    t3.printit(); 

    return 0; 
} 
+3

請刪除代碼中所有多餘的空行。 –

+2

如果有人使用沒有任何參數的構造函數會怎麼樣?那麼你會'刪除[] 0',那肯定會出錯。另外,「垃圾」是什麼意思?這個問題沒有很好解釋。 –

+0

你嘗試過使用調試器嗎? –

回答

3

你的析構函數失敗:

三個規則(也被稱爲三巨頭或法的三巨頭)是拇指在C++(之前的C++ 11的規則)聲稱,如果一個類定義了一個(或多個)以下的,應該可能顯式定義所有三個:

  • 析構函數
  • 拷貝構造函數
  • 拷貝賦值運算符
  1. 類是缺少接受test對象作爲輸入一個拷貝構造函數。你有一個轉換構造函數接受char*作爲輸入,但如果該輸入超過9個字符比在複製字符時垃圾回收周圍的內存。

  2. 您的班級是缺少一個拷貝賦值操作符,它接受一個test對象作爲輸入。你有一個將接受一個char*作爲輸入賦值運算符,但它正在所有權char*,而不是把一個複製的字符數據的的,並且是漏水以前分配的緩衝區string。更糟糕的是,它返回正在修改的test對象的副本,而不是返回參考的對象,並且該副本不會因爲你缺少拷貝構造函數的正常工作。所以最終會有多個test對象指向內存中相同的string緩衝區,所以您將在同一內存中多次使用調用delete[]的析構函數。

此外,您的operator+也被錯誤地實施。它返回一個新的test對象,但它沒有分配佔兩個源字符串總長度的新內存。您只需將右側的test對象中的字符數據直接複製到左側的test對象的char*字符串中,而無需首先展開它。所以你在搗毀周圍的記憶。這個operator+應該返回一個新的test對象,它是連接在一起的輸入字符串的副本,根本不修改任何源字符串。

嘗試一些更喜歡這個:

class test 
{ 
public: 
    // default constructor 
    test() 
     : string(0) 
    { 
    } 

    // copy constructor 
    test(const test &src) 
     : string(new char[src.starlen()+1]) 
    { 
     strcpy(string, src.string); 
    } 

    // converting constructor 
    test(const char* src) 
     : string(new char[strlen(src)+1]) 
    { 
     strcpy(string, src); 
    } 

    // destructor 
    ~test() 
    { 
     delete[] string; 
    } 

    // copy assignment operator 
    test& operator=(const test &rhs) 
    { 
     if (&rhs != this) 
      std::swap(string, test(rhs).string); 
     return *this; 
    } 

    // converting assignment operator 
    test& operator=(const char *rhs) 
    { 
     std::swap(string, test(rhs).string); 
     return *this; 
    } 

    int starlen() const 
    { 
     return strlen(string); 
    } 

    test operator+(const test& rhs) const 
    { 
     int a = starlen(); 
     int b = rhs.starlen(); 

     test ret; 
     ret.string = new char[a+b+1]; 
     strcpy(ret.string, string); 
     strcpy(ret.string+a, rhs.string); 

     return ret; 
    } 

    void printit() const 
    { 
     std::cout << string; 
    } 

private: 
    char* string; 
}; 

int main() 
{ 
    test t1("book"); 
    test t2("shelf"); 
    test t3; 
    t3 = t1 + t2; 
    cout << t3.starlen() << endl; 

    t3.printit(); 

    return 0; 
} 

如果您正在使用C++ 11或更高版本,你也應該遵循Rule of Five還有:

隨着C的來臨+ +11三條規則可以擴展爲五條規則,因爲C++ 11實現了移動語義,允許目標對象從臨時對象中獲取(或竊取)數據。以下示例還顯示了新的移動成員:移動構造函數和移動賦值運算符。因此,對於五個規則,我們有以下特殊成員:

  • 析構函數
  • 拷貝構造函數
  • 轉移構造
  • 拷貝賦值運算符
  • 移動賦值運算符
class test 
{ 
public: 
    // default constructor 
    test() 
     : string(0) 
    { 
    } 

    // copy constructor 
    test(const test &src) 
     : string(new char[src.starlen()+1]) 
    { 
     strcpy(string, src.string); 
    } 

    // converting constructor 
    test(const char* src) 
     : string(new char[strlen(src)+1]) 
    { 
     strcpy(string, src); 
    } 

    // move constructor 
    test(test &&src) 
     : string(0) 
    { 
     std::swap(string, src.string); 
    } 

    // destructor 
    ~test() 
    { 
     delete[] string; 
    } 

    // copy assignment operator 
    test& operator=(const test &rhs) 
    { 
     if (&rhs != this) 
      std::swap(string, test(rhs).string); 
     return *this; 
    } 

    // converting assignment operator 
    test& operator=(const char *rhs) 
    { 
     std::swap(string, test(rhs).string); 
     return *this; 
    } 

    // move assignment operator 
    test& operator=(test &&rhs) 
    { 
     std::swap(string, rhs.string); 
     return *this; 
    } 

    int starlen() const 
    { 
     return strlen(string); 
    } 

    test operator+(const test& rhs) const 
    { 
     int a = starlen(); 
     int b = rhs.starlen(); 

     test ret; 
     ret.string = new char[a+b+1]; 
     strcpy(ret.string, string); 
     strcpy(ret.string+a, rhs.string); 

     return ret; 
    } 

    void printit() const 
    { 
     std::cout << string; 
    } 

private: 
    char* string; 
}; 

這就是說,這可能只是一個如何編寫自定義字符串類的學習練習。但是,C++標準定義了std::string類,您應該使用它。如果你想自己的類,你可以委託給std::string,讓編譯器和STL做繁重的工作適合你:

class test 
{ 
public: 
    // default constructor 
    test() 
    { 
    } 

    // copy constructor 
    test(const test &src) 
     : str(src.str) 
    { 
    } 

    // converting constructor 
    test(const std::string &src) 
     : str(src) 
    { 
    } 

    // move constructor 
    test(test &&src) 
     : str(std::move(src.str)) 
    { 
    } 

    int starlen() const 
    { 
     return str.length(); 
    } 

    test operator+(const test& rhs) const 
    { 
     return str + rhs.str; 
    } 

    void printit() const 
    { 
     std::cout << str; 
    } 

private: 
    std::string str; 
}; 
+0

啊,3的規則再次出現了! –

+0

@MichaelDorgan它潛伏在你的視野邊緣,等待着罷工的機會。 – user4581301

0

誰的責任是它給刪除了內部字符串?我注意到你將字符串的內存分配給沒有構造函數的t3,然後在可能相同的內存地址上調用析構函數兩次。另外,你的operator +,應對很可能會寫入傳遞給它的數組的末尾。你至少應該有一個strlen()結束檢查,以防止發生。如果你損壞了你的緩衝區,那麼任何刪除[]的調用都會由於內存覆蓋堆而報錯。

請參閱Remy的答案,以獲得更好的解決方案,以正確處理字符串操作和內存分配問題(非常複雜)的性質。從我所能看到的情況來看,這個答案甚至是一個例外。