深入理解C++對象模型之拷貝構造函數

一、前言

    如果一個Class沒有聲明一個copy constructor,編譯器就會隱式聲明一個copy constructor,只有編譯器需要的時候,編譯器纔會定義一個copy constructor實例,併合成於程序之中,而編譯器需要的時候是指Class不展現出bitwise copy semantics(位逐次拷貝)即“如果一個Class未定義出copy constructor,編譯器就會自動爲它產生出一個”這句話是不對的,只有當Class不展現出bitwise copy semantics時編譯器纔會產生一個。


二、拷貝構造函數

    首先這裏列出C++Primer中關於拷貝構造函數的定義:如果一個構造函數的第一個參數是自身類類型的引用,且任何額外參數都有默認值,則次構造函數就是拷貝構造函數。即當一個Class object以另一個同類實例作爲初始值,則這個object會調用它的copy constructor,以下三種情況,會以一個object的內容作爲另一個Class object的初值:

(1)顯式的“=”號初始化

(2)object作爲函數的實參

(3)object作爲函數的返回值

    也就是說,如果Class定義了一個copy constructor,那麼以上三種情況都會調用這個copy constructor,那要是用戶沒有給Class定義一個copy constructor,編譯器是否會合成一個呢?這是由Class的“bitwise copy semantics”決定的,如果class不展現bitwise copy semantics屬性,則編譯器不會合成copy constructor,如果編譯器展現出bitwise copy semantics屬性,那麼編譯器就會合成出一個copy constructor。


三、不展現bitwise copy semantics(即編譯器要合成copy constructor)

(1)當class內含一個member class,而後者class聲明有一個copy constructor時(不論是被class設計者顯示聲明或是編譯器合成)

(2)繼承體系中,基類含有copy constructor,而派生類沒有,則編譯器需要爲派生類合成copy constructor

(3)含有虛函數的class(當編譯器導入一個虛函數指針到一個class之中時,該class就不再展現bitwise copy semantics了)

(4)繼承體系中有虛基類,則編譯器需要爲派生類合成copy constructor

其中第(1)(2)兩種情況好理解(因爲要拷貝本類,必須要先拷貝基類部分和成員類對象部分),主要在第(3)(4)兩種情況,下面將詳細解析。


四、含有虛函數的class

    如果一個類含有虛函數,則在編譯期間,這個class需要被擴張:(1)增加一個虛函數表,表內包含每一個有作用的虛函數的地址;(2)爲每一個class object安插一個指向虛函數表的指針。也就是說,class的object內存模型中就多了一個虛函數表指針的成員,請注意,一旦有了指針成員,執行bitwise copy semantics就要小心了,這會照成很多問題,一種常見的就是淺拷貝問題,當然這不是這篇文章的討論重點,讀者可以自行查閱相關資料。

    首先,要明確,在這種情況下(class 含有虛函數),編譯器合成一個copy constructor的目的是重新設定在對象拷貝過程中的虛函數表指針的值,使其指向正確的虛函數表。這裏分爲兩種拷貝情形:(1)不涉及類型交叉的對象拷貝,即同類型對象拷貝,如父類的一個對象以其另一個對象作爲初值,子類的一個對象也以其另一個對象作爲初值;(2)有類型交叉的拷貝,即繼承體系中不同層次的對象拷貝,如父類的一個對象以子類的一個對象作爲其初值。在這兩種情形中,第一種情況可以直接由bitwise copy semantics完成,因爲一個Class只有一個虛函數表,其兩個object之間的虛函數表指針可以相互進行位逐次拷貝,這是安全的,即編譯器不需要合成copy constructor。但是第二種情形就需要編譯器合成的copy constructor,因爲子類和父類的虛函數表不同,其各自object之間的虛函數表指針不能進行bitwise copy semantics,這是不安全的,那麼編譯器合成出來的copy constructor的任務就是重新設定父類的虛函數表指針,使其指向自己的(父類)虛函數表。


五、繼承體系中有虛基類

    一個class object如果以另一個object 作爲初值,而後者有一個虛基類部分(即其繼承於虛擬基類),則就會使bitwise copy semantics失效,編譯器需要合成一個copy constructor。

    同樣的,類似於含有虛函數的class,即使繼承體系中有虛基類,不涉及類型交叉的對象拷貝時,bitwise copy semantics仍然是有效的。使itwise copy semantics失效的仍然是有類型交叉的拷貝,即“父類的object以其子類的object作爲初值時”,這時編譯器需要合成一個copy constructor來設定父類對象中虛基類部分的指針或者位置偏移的初值(virtual base class pointer/offset初值)


六、總結

    與編譯器合成的default constructor不同,編譯器合成的copy constructor需要完成所有data member的copy工作,不像合成的default constructor只完成自己該完成的初始化工作,而不管那些需要有程序員來完成的工作。

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