C++構造函數中使用new時的注意事項

初級

1. 內存管理:需要在構造函數中分配內存給 ptr,並在析構函數中釋放內存,以避免內存泄漏。

MyClass::MyClass() 
{
    ptr = new int;
}

MyClass::~MyClass() 
{
    delete ptr;
}

new --- delete, new[] --- delete[]主要要匹配出現在構造和析構裏面,且多個構造函都必須統一,因爲是構造和析構匹配,而析構函數只有一個。

2. 深拷貝:如果您需要實現複製構造函數和賦值運算符重載,您需要進行深拷貝,以確保複製後的對象擁有獨立的內存空間。

MyClass::MyClass(const MyClass& other) 
{
    ptr = new int(*other.ptr);
}

MyClass& MyClass::operator=(const MyClass& other) 
{
    //需要注意判斷是否是自身,不判斷的話,other.ptr就是ptr。
    //下面先銷燬了ptr,也即釋放了other.ptr,此時再把它當右值顯然會出問題。
    if (this != &other) 
    {
        delete ptr;
        ptr = new int(*other.ptr);
    }
    return *this;
}

特別要注意這2個函數要自己實現。因爲如果代碼中沒明確實現時,C++編譯器,在需要時(代碼中有這種調用時,否則不生成),會自動生成如下成員函數:

  • 默認構造函數
  • 默認析構函數
  • 複製構造函數
  • 賦值運算符重載
  • 地址運算符重載

 其中默認構造、析構函數需要注意點在第1點中提到(貌似是廢話,入門級的規則)。

 而要實現複製構造函數的原因是:默認的複製構造函數只是簡單的將兩個對象的成員逐個賦值。顯然,如果成員是指針,那就是所謂的淺拷貝。當這個右值對象發生析構時,左值對象的指針成員還指向那片已釋放的內存,形成了懸空指針。所以有必要進行深拷貝。

 重載賦值運算符,原因:同上,也是因爲默認行爲是成員逐個賦值,淺拷貝,形成懸空指針。

 何時會發生複製構造調用?何時會發生賦值運算符重載調用?最開始我也有些暈頭,因爲兩者某些寫法看上去很相似。

以下寫法會調用複製構造
//調用默認構造
MyClass a;

//調用複製構造, 以下兩種寫法本質都是一樣,不同的習慣而已。
MyClass b(a);
MyClass b = a;
//這種實際從語法角度來理解,是發生了兩次複製構造函數調用,右邊是構造了一個臨時匿名對象,然後按上面的寫法再次調用複製構造,將此臨時匿名對象複製給對象b
//但是編譯器會做優化處理,導致只有一次調用。
MyClass b = MyClass(a);

//同理,右邊是構造了一個匿名對象
MyClass *pb = new MyClass(a);
以下寫法會調用重載的賦值操作符
//調用默認構造
MyClass a;
MyClass b;

//調用重載的賦值操作符
b = a;

 最後這句b = a是不是容易和MyClass b = a;混淆?實際後者是,聲明並構造,前者的聲明並構造已經在前2句完成了,此時b = a;不再需要產生新的對象,所以是賦值操作。聲明時既發生構造(分配內存,通過new的對象內存在堆上,局部對象在棧上,反正都是分配內存)

3. 生命週期管理:您需要確保在使用 ptr 之前,檢查指針是否爲空,以避免訪問空指針所引發的問題。

if (ptr != nullptr) 
{
    // 訪問 ptr 指向的內存
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章