- 什麼是 拷貝構造函數(Copy Constructor) ?
- 何時會調用 拷貝構造函數(Copy Constructor)?
- 編譯器什麼時候會爲我們合成一個“有用”的拷貝構造函數?
class X
{
// ...
};
X x; // 定義一個 X 對象
X xx = x; // xx 用 x 來初始化,但如果是: X xx; xx = x; 這裏調用的就是 operater = 了,原因很明顯,前面纔是初始化,這裏是賦值。
|
extern void foo(X x);
void bar()
{
X xx;
// 以 對象 xx 作爲 foo(X x) 的參數x的初值。會調用 x 的Copy Constructor,這裏的 x 是一個臨時對象。
foo(xx);
}
|
X foo_bar()
{
X xx;
// ...
return xx;
}
|
class X
{
X( const X& x);
// 也可能是多參形式,第二個參數及其後的參數有默認值 // X( const X& x, int other = 0);
}
|
class A
{
public:
A(const A& a);
private:
int a;
int b;
}
// 編譯器合成的 Copy Constructor 可能像這樣:
A::A(const A& a)
{
this->a = a.a;
this->b = a.b;
}
|
當一個 class 沒有顯示定義 copy constructor 的時候,編譯器會在必要的時候爲這個class合成出來。所謂的必要時,是指 class 沒有展現出 Bitwise Copy Semantics(位逐次拷貝) 時。而當 class
展現出 位逐次拷貝 時,並不需要合成一個 Copy Constructor,默認的 Memberwise Initialization 就已足夠(但有風險:指針問題)。
|
class Base
{
public:
Base();
virtual ~Base(); // 基類的析構函數一定要 virtual,否則會導致嚴重後果。
virtual void draw();
};
class Derive : public Base
{
public:
Derive();
void draw(); // 雖沒寫明 virtual,但實際上是 virtual。
};
Derive yogi;
Derive winnie = yogi; // OK! winnie 和 yogi 的 vtbl 地址相同。可以直接拷貝 vptr 的值。
Base franny = yogi; // 注意,franny 的 vptr 和 yogi 的 vptr 指向不同的 vtbl,因此,copy contructor 需要重新設定 franny 的值,防止其指向 Derive 的vtbl.
// 這裏還涉及到以子類初始化基類時的切割問題
|
class Raccoon : public virtual ZooAnimal
{
// ...
}
class RedPanda : public Raccoon
{
// ...
}
|