2011-01-10 121 views
3

考慮下面的代碼:瞭解C++動態分配

class CString 
{ 
private: 
    char* buff; 
    size_t len; 

public: 
    CString(const char* p):len(0), buff(nullptr) 
    { 
     cout << "Constructor called!"<<endl; 
     if (p!=nullptr) 
     { 
      len= strlen(p); 
      if (len>0) 
      { 
       buff= new char[len+1]; 
       strcpy_s(buff, len+1, p);    
      }   
     }  
    } 

    CString (const CString& s) 
    { 
     cout << "Copy constructor called!"<<endl; 
     len= s.len; 
     buff= new char[len+1]; 
     strcpy_s(buff, len+1, s.buff);  
    } 

    CString& operator = (const CString& rhs) 
    { 
     cout << "Assignment operator called!"<<endl; 
     if (this != &rhs) 
     { 
      len= rhs.len; 
      delete[] buff;   
      buff= new char[len+1]; 
      strcpy_s(buff, len+1, rhs.buff); 
     } 

     return *this; 
    } 

    CString operator + (const CString& rhs) const 
    { 
     cout << "Addition operator called!"<<endl; 

     size_t lenght= len+rhs.len+1; 
     char* tmp = new char[lenght]; 
     strcpy_s(tmp, lenght, buff); 
     strcat_s(tmp, lenght, rhs.buff); 

     return CString(tmp); 
    } 

    ~CString() 
    { 
     cout << "Destructor called!"<<endl; 
     delete[] buff; 
    }  
}; 

int main() 
{ 
CString s1("Hello"); 
CString s2("World"); 
CString s3 = s1+s2;  
} 

我的問題是,我不知道如何刪除加法運算功能(char* tmp = new char[length])分配的內存。我無法在構造函數中執行此操作(我嘗試了delete[] p),因爲它也是從主函數調用的,並將字符數組作爲參數分配給堆中的參數......我怎樣才能解決這個問題?

+0

你還需要在你的賦值運算符的工作(它不安全(喜歡複製和交換Idium))。見下文。 – 2011-01-10 20:59:00

回答

4

加法函數應該返回一個CString,而不是CString &。在添加函數中,應該構造返回值,然後刪除[] temp,因爲它不再需要,因爲在CString類中創建內存副本。

CString operator + (const CString& rhs) const 
{ 
    cout << "Addition operator called!"<<endl; 

    size_t lenght= len+rhs.len+1; 
    char* tmp = new char[lenght]; 
    strcpy_s(tmp, lenght, buff); 
    strcat_s(tmp, lenght, rhs.buff); 

    CString retval(tmp); 
    delete[] tmp; 
    return retval; 
} 
+0

那麼你在非靜態方法中創建一個CString的新實例?我不認爲這是一個很好的實現 – trojanfoe 2011-01-10 13:16:07

+2

@trojanfoe:這就是operator +通常被實現的方式。除了創建一個新的CString,你還會怎樣返回一個CString? – Puppy 2011-01-10 13:17:11

+0

@DeMem非常感謝你!它畢竟簡單,我應該做的就是創建對象而不立即返回,以便有時間刪除數組! – kiokko89 2011-01-10 13:18:17

0
CString& operator + (const CString& rhs) const 

{ 
    cout << "Addition operator called!"<<endl; 

    size_t lenght= len+rhs.len+1; 
    char* tmp = new char[lenght]; 
    strcpy_s(tmp, lenght, buff); 
    strcat_s(tmp, lenght, rhs.buff); 
    CString tempObj(tmp); 
    delete [] tmp; 
    return tempObj; 
} 

例如,

0

首先,operator+應該返回新的對象,而不是修改的+操作數之一,以更好地應被聲明爲一個非成員(可能是朋友)功能。首先執行operator+=,然後使用它 - operator+,你不會有這個問題。

CString operator+(CString const& lh, CString const& rh) 
{ 
    CString res(lh); 
    return res += rh; 
} 
2

問題:

在你的賦值運算符你未能提供任何異常的保證。在確保操作成功之前,您正在刪除緩衝區。如果出現問題,對象將處於未定義狀態。

CString& operator = (const CString& rhs) 
{ 
    cout << "Assignment operator called!"<<endl; 
    if (this != &rhs) 
    { 
     len= rhs.len; 
     delete[] buff;   
     buff= new char[len+1]; /// BOOM 

     // If you throw here buff now points at undefined memory. 
     // If this is an automatic variable the destructor is still going 
     // to be called and you will get a double delete. 

     // All operations that can fail should be done BEFORE the object is modified. 

     strcpy_s(buff, len+1, rhs.buff); 
    } 

    return *this; 
} 

我們可以通過移動東西(並使用temp)來糾正這些問題。

CString& operator = (const CString& rhs) 
{ 
    cout << "Assignment operator called!"<<endl; 
    if (this != &rhs) 
    { 
     char* tmp = new char[len+1]; 
     strcpy_s(tmp, rhs.len+1, rhs.buff); // for char this will never fail 
              // But if it was another type the copy 
              // may potentially fail. So you must 
              // do the copy before changing the curren 
              // objects state. 

     // Now we can change the state of the object safely. 
     len= rhs.len; 
     std::swap(tmp,buff); 

     delete tmp; 
    } 

    return *this; 
} 

一個更好的解決方案是使用複製和交換idium:

CString& operator = (CString rhs) // Note pass by value to get auto copy. 
{         // Most compilers will then do NRVO 
    this->swap(rhs); 
    // Simply swap the tmp rhs with this. 
    // Note that tmp was created with copy constructor. 
    // When rhs goes out of scope it will delete the object. 
} 

void swap(CString& rhs) 
{ 
    std::swap(len, rhs.len); 
    std::swap(buff, rhs.buff); 
} 

現在,讓我們處理您的+運營商

CString operator + (const CString& rhs) const 
{ 
    // You could optimize this by providing a private constructor 
    // that takes two char pointers so that allocation is only done 
    // once. 
    CString result(*this); 
    return result += rhs; 
} 

CString operator += (const CString& rhs) 
{ 
    size_t lenght= len+rhs.len+1; 

    // Char are easy. No chance of failure. 
    // But if this was a type with a copy constructor or any other complex 
    // processing involved in the copy then I would make tmp a smart pointer 
    // to make sure that it's memory was not leaked if there was an exception. 
    char* tmp = new char[lenght]; 

    strcpy_s(tmp, lenght, buff); 
    strcat_s(tmp, lenght, rhs.buff); 

    std::swap(len, length); 
    std::swap(buff, tmp); 

    delete tmp; 
}