C++學習-類和對象(三)

拷貝構造

創建對象時使用同類對象來進行初始化,這時所用的構造函數稱爲拷貝構造函數(Copy Constructor),拷貝構造函數是特殊的構造函數

就是使用一個已經存在的對象去創建一個新的對象。新的對象是舊的對象的一份拷貝。

  1. 拷貝構造函數其實是一個構造函數的重載。
  2. 拷貝構造函數的參數必須使用引用傳參,使用傳值方式會引發無窮遞歸調用。
  3. 若未顯示定義,系統會默認缺省的拷貝構造函數。默認生成(淺拷貝:一個字節一個字節拷貝)依次拷貝類成員進行初始化。
//使用默認的拷貝構造(淺拷貝)
#include<iostream>
using namespace std;
class Data

{
    public:



        //帶參的缺省的構造函數

        Data(int year=1900,int month=1,int day=1)
        {
            _year=year;
            _month=month;
            _day=day;

        }

        void Display()
        {
            cout<<_year<<"-"<<_month<<"-"<<_day<<endl;
        }
        ~Data()//析構函數
        {

        }


    private:
        int _year;
        int _month;
        int _day;

};
int main()
{


    Data d1(2018,1,1);//調用構造函數

    Data d2(d1);//調用拷貝構造函數
    //Data d2=d1 和上面是等價的

    d1.Display();
    d2.Display();
    return 0;

}
[zyc@localhost lession_class]$ ./a.out 
2018-1-1
2018-1-1

結果分析:

利用d1.我們拷貝構造了一個對象d2。


默認拷貝構造的缺陷

來看下面例子

#include<iostream>
#include <stdlib.h>
using namespace std;
class Array
{
    public:
        //構造函數
        Array()
        {
            cout<<"Array()"<<endl;
            _ptr=(int *)malloc(2*sizeof(int));

        }

        //這裏的析構函數需要完成清理工作(釋放空間)

        ~Array()
        {


                cout<<"~Array()"<<endl;
                free(_ptr);
                _ptr=NULL;

        }


    private:
        int *_ptr;

};
int main()
{
    Array ptr;
    return 0;

}

編譯通過,調用一次構造函數,一次析構函數


[zyc@localhost lession_class]$ ./a.out 
Array()
~Array()

類在我們沒有定義拷貝構造函數的時候,會默認定義默認拷貝構造函數,也就是說可以直接用同類型的類間可以相互賦值、初始化:

int main()
{
    Array p1;
    Array p2=p1;
    return 0;

}

這個時候,編譯器機會報錯,原因就是因爲同一塊空間被釋放了兩次

這裏寫圖片描述
這裏寫圖片描述

類的默認拷貝構造函數只會用被拷貝類的成員的 值 爲拷貝類簡單初始化,也就是說二者的p指針指向的內存空間是一致的。以前面Array可以知道,編譯器爲我們默認定義的拷貝構造函數爲:

Array(const Array& Array)
{
    p1 = Array.p1;
    pp2 = Array.p1;      //兩個類的p指針指向的地址一致。
}

main函數將要退出時,拷貝類p2的析構函數先得到執行,它把自身p指向的堆空間釋放了;接下來,p1的析構函數得到調用,被拷貝類p1的析構函數得到調用,它同樣要去析構自身的p指向指向的堆空間,但是該空間和p2類中p指向的空間一樣,造成重複釋放,程序運行崩潰。

這裏寫圖片描述

爲了解決這種問題,我們可以使用自定義拷貝構造函數,裏面用深度拷貝的方式爲拷貝類初始化

深拷貝原理

這裏寫圖片描述

關於深拷貝後面再介紹。

自定義拷貝構造函數,也可以定義成簡單的值拷貝,即淺拷貝。

class Data

{
    public:



        //帶參的缺省的構造函數

        Data(int year=1900,int month=1,int day=1)
        {
            _year=year;
            _month=month;
            _day=day;

        }
        Data(const  Data& d)//拷貝構造函數,這裏必須使用傳引用
        {
            this->_year=d._year;
            this->_month=d._month;
            this->_day=d._day;
        }
        void Display()
        {
            cout<<_year<<"-"<<_month<<"-"<<_day<<endl;
        }
        ~Data()//析構函數
        {

        }


    private:
        int _year;
        int _month;
        int _day;

};

拷貝構造函數 需要注意的問題

拷貝構造函數的參數必須使用引用傳參,使用傳值方式會引發無窮遞歸調用。
傳引用,否則無限遞歸

舉例:假設是傳參時使用傳值方式。


class Data
{
    public:
        //構造函數
        Data()
        {
            cout<<"Data()"<<endl;

        }
        //拷貝構造函數,以傳值方式
        Data(const Data d )
        {
            cout<<"Data(const Data &d)"<<endl;
        }
        ~Data()
        {
                cout<<"~Data()"<<endl;
        }
    private:
        int a; 

};
int main()
{
    Data d1;
    Data d2=d1;
    return 0;

}

運行結果:會提示報錯信息。編不過

[zyc@localhost lession_class]$ g++ tmp.cpp -g
tmp.cpp:21: error: invalid constructor; you probably meant ‘Data (const Data&)’

原因:
這裏寫圖片描述

將傳值應用更改成傳引用。

Data(const Data &d )

運行結果

[zyc@localhost lession_class]$ ./a.out 
Data()
Data(const Data &d)
~Data()
~Data()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章