1. 程式人生 > >C++_深淺拷貝

C++_深淺拷貝

一、何時呼叫拷貝建構函式

1、物件在建立時使用其他的物件初始化 
     Person p(q); //此時複製建構函式被用來建立例項p 
     Person p = q; //此時複製建構函式被用來在定義例項p時初始化p

2、物件作為函式的引數進行值傳遞時

    f(p); //此時p作為函式的引數進行值傳遞,p入棧時會呼叫複製建構函式建立一個區域性物件,與函式內的區域性變數具有相同的作用域。

注意:需要注意的是,賦值並不會呼叫複製建構函式,賦值只是賦值運算子(過載)在起作用
           p = q; //此時沒有複製建構函式的呼叫! (記住初始化和賦值的區別)
           簡單來記的話就是,如果物件在宣告的同時將另一個已存在的物件賦給它,就會呼叫複製建構函式;如果物件已經存在,            然後將另一個已存在的物件賦給它,呼叫的就是賦值運算子(過載)


二、解析

1.在未定義顯示拷貝建構函式的情況下,系統會呼叫預設的拷貝函式–即淺拷貝,它能夠完成成員的一對一拷貝(逐位複製),當類中資料成員沒有指標時,利用淺拷貝完全沒問題的;但當資料成員中有指標時,如果採用簡單的淺拷貝,那麼兩個類中的兩個指標將會指向同一塊地址,當物件快結束時,會呼叫兩次析構器,從而導致指標懸掛現象,所以此時必須使用深拷貝 
2.簡單來說,帶指標用深拷貝,不帶指標用淺拷貝


 

1.無指標的淺拷貝


class A
{
    public:
        A(int _data):data(_data){}
        A(){}
    private:
        int data;   
};

int main()
{
    A a(5);
    A b=a;//淺拷貝 
}
//解釋:b=a;就是淺拷貝,執行完b.data=5;如果物件中沒有其他資源(如:堆,檔案,系統資源),深淺無差。

 2.有指標的淺拷貝(導致記憶體洩露)

class A
{
    public:
        A(int _size):size(_size)
        {
            data=new int[size];
        }//給data分配size個記憶體 
        A(){}
        ~A()
        {
            delete []data;
        }//析構時釋放資源 
    private:
        int *data;
        int size;   
};

int main()
{
    A a(5);
    A b=a;
}

這裡b=a會造成未定義行為,因為類A中拷貝構造器是編譯器生成的,所以b=a執行的是淺拷貝(記住:淺拷貝是物件資料之間的簡單賦值)例如:b.size=a.size和b.data=a.data
這裡b的指標data和a的指標指向了堆上的同一塊記憶體,a和b析構時同一塊記憶體將會被釋放兩次,其結果是,有未定義的記憶體將會被洩露或程式奔潰!!!

3.有指標的深拷貝,(解決上面的問題)物件另開闢一塊記憶體

class A
{
    public:
        A(int _size):size(_size)
        {
            data=new int[size];
        }//給data分配size個記憶體 
        A(){}
        A(const A&_A):size(_A.size)
        {
            data=new int[size];
        }//深拷貝 
        ~A()
        {
            delete []data;
        }//析構時釋放資源 
    private:
        int *data;
        int size;   
};

int main()
{
    A a(5);
    A b=a;
}

以上程式碼就沒有問題了,與淺拷貝區別就是,在自己定義的拷貝函式裡另開闢一塊記憶體即深拷貝。

轉載https://blog.csdn.net/w_linux/article/details/65450857