1.有關(guān) private virtuals:幾乎不用。如果沒有特殊的原因,不提倡使用。
2.當(dāng)基類構(gòu)造函數(shù)調(diào)用虛函數(shù)時(shí),為什么不調(diào)用派生類重寫的該虛函數(shù)?這樣做是危險(xiǎn)的,C++會(huì)阻止你這樣做
coder:
#include <iostream>
#include <string>
void println(const std::string& msg)
{ std::cout << msg << '\n'; }
class Base {
public:
Base() { println("Base::Base()"); virt(); }
virtual void virt() { println("Base::virt()"); }
};
class Derived : public Base {
public:
Derived() { println("Derived::Derived()"); virt(); }
virtual void virt() { println("Derived::virt()"); }
};
int main()
{
Derived d;
...
}
程序輸出:
Base::Base()
Base::virt() // ← Not Derived::virt()
Derived::Derived()
Derived::virt()
當(dāng)基類被構(gòu)造時(shí),對象還不是一個(gè)派生類的對象,所以如果 Base::Base()調(diào)用了虛函數(shù) virt(),則 Base::virt() 將被調(diào)用,即使 Derived::virt()(即派生類重寫的虛函數(shù))存在。
同樣,當(dāng)基類被析構(gòu)時(shí),對象已經(jīng)不再是一個(gè)派生類對象了,所以如果 Base::~Base()調(diào)用了virt(),則 Base::virt()得到控制權(quán),而不是重寫的 Derived::virt() 。
如果 Base::Base()調(diào)用了虛函數(shù) virt(),這個(gè)規(guī)則使得 Base::virt()被調(diào)用。如果不按照這個(gè)規(guī)則,Derived::virt()將在派生對象的派生部分被構(gòu)造之前被調(diào)用,此時(shí)屬于派生對象的派生部分的某個(gè)成員對象還沒有被構(gòu)造,而 Derived::virt()卻能夠訪問它。這將是災(zāi)難。
3.模擬動(dòng)態(tài)綁定在一個(gè)基類的構(gòu)造里的方法
class Base {
public:
Base();
...
virtual void foo(int n) const; // often pure virtual
virtual double bar() const; // often pure virtual
// if you don't want outsiders calling these, make them protected
};
Base::Base()
{
... foo(42) ... bar() ...
// these will not use dynamic binding
// goal: simulate dynamic binding in those calls
}
class Derived : public Base {
public:
...
virtual void foo(int n) const;
virtual double bar() const;
};
實(shí)現(xiàn)方法有兩種,根據(jù)自己實(shí)際情況來選擇。
第一種方法,把初始化分兩個(gè)階段,第一階段是實(shí)際的構(gòu)造,第二階段是"init"方法。動(dòng)態(tài)綁定就在第二階段,第二階段是構(gòu)造概念的一部分,所以我們可以簡單的把從 Base::Base() 移到 Base::init()
class Base {
public:
void init(); // may or may not be virtual
...
virtual void foo(int n) const; // often pure virtual
virtual double bar() const; // often pure virtual
};
void Base::init()
{
... foo(42) ... bar() ...
// most of this is copied from the original Base::Base()
}
class Derived : public Base {
public:
...
virtual void foo(int n) const;
virtual double bar() const;
};
剩下的就是確定哪里調(diào)用第一階段,哪里調(diào)用第二階段。這里我們要注意以下兩點(diǎn):第一在創(chuàng)建對象時(shí),要加上小小的約束條件,尤其在同一層中有一處或兩處要?jiǎng)?chuàng)建
創(chuàng)建時(shí),這個(gè)約束可以確保程序不會(huì)出錯(cuò)。第二在第一階段創(chuàng)建對象的方法有三種,是new Derived或是聲明Derived對象,或是不知道具體的類型(通過虛構(gòu)造或
類工廠創(chuàng)建),用第三種是強(qiáng)壯的,這樣可是你很容易的插入一個(gè)Derived對象。
在第一階段,對象的創(chuàng)建一般在堆上,這時(shí)我們需要保存一個(gè)管理指針(智能指針std::auto_ptr,引用計(jì)數(shù)的指針,或是其他的析構(gòu)刪除),最好的預(yù)防堆溢出的方法是在第二階段是拋出異常。分配堆空間簡單的示例代碼如下:
#include <memory>
void joe_user()
{
std::auto_ptr<Base> p(/*...somehow create a Derived object via new...*/);
p->init();
...
}
第二種方案是組合joe_user前兩句到create函數(shù)里。如果你是用工廠(factory)模式,譬如虛構(gòu)造,你可以這兩行放在靜態(tài)方法中調(diào)用Base::create(): 代碼如下
#include <memory>
class Base {
public:
...
typedef std::auto_ptr<Base> Ptr; // typedefs simplify the code
static Ptr create();
...
};
Base::Ptr Base::create()
{
Ptr p(/*...use a factory to create a Derived object via new...*/);
p->init();
return p;
}
它簡化了joe_user的功能,更重要的是可以在創(chuàng)建對象不用調(diào)用Init().
void joe_user()
{
Base::Ptr p = Base::create();
...
}
我們在這個(gè)方法中,我們應(yīng)當(dāng)竭力避免調(diào)用Init(),那么就應(yīng)該使派生類的構(gòu)造、拷貝構(gòu)造成為priviate或protected;
最后的方法,則和上面的都不同,在第二層類結(jié)構(gòu)中加入foo()和bar(). 如果這兩個(gè)函數(shù)要存取Derived的數(shù)據(jù)時(shí),這個(gè)方法是不能用的。
class Helper {
public:
virtual void foo(int n) const = 0;
virtual double bar() const = 0;
};
class Helper1 : public Helper {
public:
virtual void foo(int n) const;
virtual double bar() const;
};
class Helper2 : public Helper {
public:
virtual void foo(int n) const;
virtual double bar() const;
};
Base類也要?jiǎng)h除init(),Base類和Derived類要?jiǎng)h除foo() and bar(),在Base類構(gòu)造中加入Helper的引用
class Base {
public:
Base(const Helper& h);
... // remove
... // remove
};
class Derived : public Base {
public:
... // remove
};
當(dāng)我們定義Base::Base(const Helper&)時(shí),它會(huì)正確調(diào)用h.foo(42)和h.bar()
Base::Base(const Helper& h)
{
... h.foo(42) ... h.bar() ...
// almost identical to the original Base::Base()
// but with h. in calls to h.foo() and h.bar()
}
Derived::Derived()
: Base(Helper2()) // ←the magic happens here
{
...
}
注意:Derived可以傳遞值到Helper的派生類的構(gòu)造中,但不是任何的數(shù)據(jù)都可以傳至Helper派生類。比如Helper::foo()和 Helper::bar() 就不能存取數(shù)據(jù)在這個(gè)類中,特別是數(shù)據(jù)是Derived類中中聲明的數(shù)據(jù)。
Helper派生類也可以做成類似joe_user功能,例如:
Derived::Derived(const Helper& h)
: Base(h)
{
...
}
如果Helper不需要數(shù)據(jù),那么可以通過一個(gè)靜態(tài)方法來替代它。
class Base {
public:
typedef void (*FooFn)(int); // typedefs simplify
typedef double (*BarFn)(); // the rest of the code
Base(FooFn foo, BarFn bar);
...
};
Base::Base(FooFn foo, BarFn bar)
{
... foo(42) ... bar() ...
// almost identical to the original Base::Base()
// except calls are made via function pointers.
}
class Derived : public Base {
public:
Derived();
static void foo(int n); // the static is important!
static double bar(); // the static is important!
...
};
Derived::Derived()
: Base(foo, bar) // ←pass the function-ptrs into Base's ctor
{
...
}