C++物件模型之 Copy Constructor的建構操作
序言:
在計算機當中, 當一個class object的內容作為另一個class object的初始值的時候, 如果class 設計者沒有顯示的宣告Copy Constructor, 那麼編譯器將會自動生成Copy Constructor。
那麼在這裡問題就來了:
1、 在什麼情況下會呼叫Copy Constructor?
2、在初始化過程中, 是採用位逐次拷貝還是採用成員初始化的方式呢?
在什麼情況下會呼叫Copy Constructor
1、一個明確的class object的內容作為另一個class object的初始值。
2、類的物件本身作為函式的引數(非引用和指標的情況)
3、函式返回值是一個類的物件(如果可能在編譯器優化的過程中不做明顯呼叫)[在VS下會出現情況, 但是在dev下不會出現此情況]
位逐次拷貝或成員初始化形式的Copy Constructor
位逐次拷貝Copy Constructor:按照類成員的宣告次序, 呼叫memcpy函式進行拷貝
成員初始化Copy Constructor:按照類成員的宣告次序,以此呼叫起建構函式進行初始化
他們之間的區別:
1、如果類成員中包含指標, 通過位逐次拷貝的新物件的成員指標指向的相同的空間。
2、在虛指標的情況下, 如果是父類和基類之間的構造,很有可能會造成虛指標亂指。
例子如下:
class Myclass{
public:
//...
private:
int a;
char *str;
};
如果是位逐次拷貝的情況下複製建構函式可能合成的虛擬碼
Myclass ::Myclass(const Myclass & _my){
//合成的偽碼
this->a = _my.a;
this->str = _my.str;
}
如果是成員初始化的情況下複製建構函式可能合成的虛擬碼
Myclass ::Myclass(const Myclass & _my){ //合成的偽碼 this->a.int::int(_my.a); this->str = new char[strlen(_my.str) + 1]; memset(this->str, 0, strlen(_my.str) + 1); memcpy(this->str,_my.str, sizeof(char) * (strlen(_my.str))); }
如何使C++不展現出位逐次拷貝
1、該類含有member object, 並且該member object的class中聲明瞭複製建構函式(不管是class設計者宣告或者是編譯器自動生成的)
2、該類繼承與一個有複製建構函式的基類(不管是class設計者宣告或者是編譯器自動生成的)
3、class 聲明瞭一個或者多個virtual function
4、當class派生自一個繼承鏈, 並且其中有一個或者多個virtual base class
一、編譯器如何重新設定virtual table的指標
當class聲明瞭一個或者多個virtual function的時候, 在生成複製建構函式將有兩個擴張的操作:
(1)、增加一個virtual function table(vtbl), 內含每一個有作用的virtual function的地址
(2)、將一個指向virtual function table的指標(vptr), 安插在每一個class object內
當我們在類中當中加入virtual function的時候, 那麼該類就不表現為位逐次拷貝, 那麼編譯器將會自動合成複製建構函式來對虛表和虛指標進行初始化。
class ZooAnimal{
public:
ZooAnimal(){cout << "ZooAnimal Constructor\n";}
virtual ~ZooAnimal(){cout << "ZooAnimal Destructor\n";}
virtual void animate(){cout << "ZooAnimal animate\n"; }
virtual void draw(){cout << "ZooAnimal draw\n"; }
private:
// member data
};
class Bear:public ZooAnimal{
public:
Bear(){cout << "Bear Constructor\n";}
~Bear(){cout << "Bear Destructor\n";}
void animate(){cout << "Bear animate\n"; }
void draw(){cout << "Bear draw\n"; }
virtual void dance(){cout << "Bear dance\n"; }
private:
// member data
};
當ZooAnimal object 以另一個物件作為初始值, 或者 Bear object以另外一個物件作為初值, 都可以直接考“位逐次拷貝”, 這樣是安全的
Bear b1;
Bear b2 = b1;
但是如果執行以下程式碼,
Bear b1;
ZooAnimal z1 = b1;
編譯器生成的複製建構函式並不會直接拷貝Bear object當中的虛表內容, 會發生切割的行為, 導致ZOoAnimal會明確設定物件object的vptr指向ZOoAnimal class的virtual table的內容, 而不是直接拷貝遊走
二、總結
1、如果一個class 未定義copy constructor函式, 那麼編譯器將會自動生成---這個說法是錯誤的
2、Default constructor和Copy Constructor在必要的時候由編譯器自動合成。
3、一個class object有兩個方式構成複製建構函式: 一種是初始化的方式另一種是assignment過載。