拷貝構造函數 Copy Constructor

1,對象本體 與 實體

    例如:     

    int a = 1;

        本體 a

        實體 1

        本體與實體一致

    int *a, b = 1;
    a = &b;

        本體 a

        實體 a所指向的空間

        本體與實體不一致

2,當對象本體與實體一致時,如:

    class Person
    {
        int age;
    public:
        Person(int arg = 18):age(arg){ cout << "Constructing." <<  endl;
        ~Person(){ cout << "Deconstructing." << endl; }
    };

    int main()
    {
        Person p1(20);
        Person p2(p1); // 或Person p2 = p1;

        return 0;
    }

輸出:

    Constructing
    Deconstructing
    Deconstructing

會只調用構造函數一次,但調用析構函數兩次。實際上,當定義p1時,會調用構造函數,此時輸出一次"Constructing.",而當以p1創建p2時,不會調用構造函數,會直接把p1的本體拷貝給p2。對於Person類而言,本體與實體一致,故

    Person p1(20);
    Person p2(p1);

相當於:

    int a = 1;
    int b = a;

3,當對象本體與實體不一致時,如:

運行結果:

在上述代碼中,我們在類的構造函數中爲成員變量pName創建了內存,在析構函數中進行釋放。運行結果表明程序出錯。這是因爲當我們直接使用對象p1來創建對象p2時,並不會再次調用構造函數,也就不會爲p2的成員變量pName分配內存,而是直接將本體進行了複製,即只複製了p1的成員變量pName的值而已。故而,當析構了p2再析構p1時,之前的內存已經被釋放了,p1對象再釋放內存時內存是不存在的,最終導致程序運行出錯。這說明,的確只是將本體進行了複製而已。當本體與實體一致時,程序運行還正常,當不一致時,就會出錯。

4,注意:當使用賦值(=)或p2(p1)這種形式創建對象時,也會創建相應的對象,只是對於成員變量中本體與實體不一致的情況,會僅僅進行本體的複製。見如下代碼:

運行結果如下:

我們看到,兩個對象的地址不一樣。我們知道this其實是一個隱含的指針,指向調用對象,比如:myclass my;,則this實際上就是指向my的指針,所存儲的就是my的地址。所以,此處輸出的this裏的值不一樣,說明的確是創建了新的對象,而且把本體與實體一致的參數Age的值進行了正確的複製。

但是,但類的成員變量中有本體與實體不一樣的情況時,就會出錯,看如下代碼:

毫無疑問,程序出錯。結果如下:

從結果中看到,當p2修改了姓名後,實際上修改的是p1的姓名,也就是說,實際上並沒有爲p2存儲姓名分配空間,僅僅是將p1的pName值進行了簡單複製。這就是本體與實體不一致的情況,如下圖所示:

那麼,對於本體與實體一致的情況,真的爲p2中的變量分配內存了麼?我們修改一下程序,輸出相關內容進行驗證。

程序修改如下:

運行結果:

運行結果說明,的確創建了對象,而且當本體與實體一致時,對象的複製操作也沒有問題。當不一致時,就會導致僅僅把本體進行了複製,將通過複製創建的對象中的本體也指向了被複制的對象,造成錯誤。

5,不一致的情況還想拷貝創建對象,怎麼辦?

    此時可以通過重寫拷貝構造函數來完成目的。

    程序如下:

拷貝構造函數本身是構造函數的重載,需要注意的是,拷貝構造函數的參數必須是該類對象的常量引用,並且,和默認構造函數一樣,提供了拷貝構造函數後,默認的拷貝構造函數就不存在了。錢老爺子說,自定義的對象作爲參數傳遞時,能用引用就用引用,能用常量引用的儘量使用常量引用。

當對象的本體與實體不一致時,自定義拷貝構造函數才具有意義,否則,默認拷貝構造函數就夠用了。

6,注意:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章