天天看點

關于C++預設拷貝構造函數産生的問題的讨論

對于拷貝構造函數,我前面的博文有提起過,不過,有的時候,淺拷貝和深拷貝真的很難了解,是以,我們再進行關于拷貝構造函數的一些讨論。

大家都整到拷貝構造函數分為淺拷貝和深拷貝,系統預設的拷貝構造函數是淺拷貝。

預設拷貝構造函數以記憶體拷貝的方式将舊有對象的記憶體空間拷貝到新對象的記憶體空間。

如果類中有指針類型的類型的時候,預設拷貝構造函數隻能複制指針屬性的值,而不能複制指針屬性所指向的記憶體,此時,如果我們自己不顯式定義拷貝構造函數,那麼我們在程式設計的時候,可能會出現很詭異的問題。

顯式定義拷貝構造函數來完成指針屬性等需要特殊處理的屬性的拷貝工作。

The Number one :  我們先來看淺拷貝帶來的問題

---------------------我是分割線------------------------

# include <iostream>
using namespace std;

class Car
{
private:
    char*  brand;
    float  price;
public:
    Car(const char* sz, float p)       
    {
        //構造函數中為brand配置設定記憶體
        brand = new char[strlen(sz)+1];
        strcpy(brand, sz);
    }
    ~Car
    {
        //析構函數中釋放申請的記憶體
        delete[] brand;
        cout << " Clear is over ! " << endl;        
    }
    void just_print()
    {
        cout << "brand : " << brand << endl;
        cout << "price : " << price << endl;
    }
};

int main(void)
{
    Car car_one("BMW",120);
    car_one.just_print();
    //調用預設的拷貝構造函數
    Car car_two(comp_one);
    car_two.print();

    return 0;
}      

----------------------------------------------------------------------------

這個程式運作失敗,代碼分析:

1、car_two(car_one)等價于

         car_two.brand = car_one.brand;

         car_two.price  = car_one.price;

2、經過指派操作後,兩個對象中的指針指向的是同一塊動态記憶體,當car_one和car_two撤銷時,其釋放函數都要釋放同一塊動态記憶體記憶體,可是,兩個對象撤銷有先有後,一旦一個對象被撤銷,另一個對象的brand指針變速"野指針",使用該指針再次釋放同一塊動态記憶體會引發記憶體錯誤。

不僅僅是重複釋放記憶體的問題,還會出現其他問題:

-------------------------------------------------------------------------------

int main(void)
{
    Car car_one("Dell", 7000);
    
    if(true)
    {
        car car_two(car_one);
        car_two.print();
    }
    //car_one.brand指向的動态記憶體此時已經被釋放
    car_one.print();
    
    return 0;
}      

-------------------------------------------------------------------------------------------

由于car_two是在if結構中定義的局部對象,是以if結構退出時,car_two被撤銷,系統自動調用其析構函數,釋放了car_two.brand所指向的動态記憶體,由于car_one和car_two值相同,此時car_one.brand已無所指,成了野指針,此時,對該指針的讀寫操作都會引發無法預料的錯誤。

此時,我們就需要自己來定義拷貝構造函數:

//顯式定義構造函數
# include <iostream>
# include <cstring>
using namespace std;

class Car
{
private:
    char*    brand;
    float    price;
public:
    Car(const char*  sz, float  p)    
    {
	brand = new char[strlen(sz)+1];
        strcpy(brand, sz);
        price = p;        
    }
    //自定義拷貝構造函數
    Car(const  Car&  cp)
    {
	//重新為brand開辟與cp.brand同等大小的記憶體空間
	brand = new char[strlen(cp.brand) + 1];
        //
        strcpy(brand, cp.brand);
        price = cp.price;        
    }
    ~Car()
    {
	delete[]  brand;
        cout << "clear over " <<endl;		 
    }
    
    void print()
    {
	cout << "brand " << endl;
        cout << "price " << endl; 
    }    
};

int main(void)
{
    Car car_one("Dell", 8999);
    car_one.print();
    //
    Car car_two(car_one);
    car_two.print();
    //沒有采用brand = cp.brand 這種直接直接指派,而是重新申請動态記憶體,使用
    //庫函數strcpy實作了字元串的複制
    //car_one.brand和car_two.brand指向兩塊不同的記憶體,避免了錯誤
         
    return 0;
}      

繼續閱讀