1.有关 private virtuals:几乎不用。如果没有特殊的原因,不提倡使用。
2.当基类构造函数调用虚函数时,为什么不调用派生类重写的该虚函数?这样做是危险的,C++会阻止你这样做
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()
当基类被构造时,对象还不是一个派生类的对象,所以如果 Base::Base()调用了虚函数 virt(),则 Base::virt() 将被调用,即使 Derived::virt()(即派生类重写的虚函数)存在。
同样,当基类被析构时,对象已经不再是一个派生类对象了,所以如果 Base::~Base()调用了virt(),则 Base::virt()得到控制权,而不是重写的 Derived::virt() 。
如果 Base::Base()调用了虚函数 virt(),这个规则使得 Base::virt()被调用。如果不按照这个规则,Derived::virt()将在派生对象的派生部分被构造之前被调用,此时属于派生对象的派生部分的某个成员对象还没有被构造,而 Derived::virt()却能够访问它。这将是灾难。
3.模拟动态绑定在一个基类的构造里的方法
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;
};
实现方法有两种,根据自己实际情况来选择。
第一种方法,把初始化分两个阶段,第一阶段是实际的构造,第二阶段是"init"方法。动态绑定就在第二阶段,第二阶段是构造概念的一部分,所以我们可以简单的把从 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;
};
剩下的就是确定哪里调用第一阶段,哪里调用第二阶段。这里我们要注意以下两点:第一在创建对象时,要加上小小的约束条件,尤其在同一层中有一处或两处要创建
创建时,这个约束可以确保程序不会出错。第二在第一阶段创建对象的方法有三种,是new Derived或是声明Derived对象,或是不知道具体的类型(通过虚构造或
类工厂创建),用第三种是强壮的,这样可是你很容易的插入一个Derived对象。
在第一阶段,对象的创建一般在堆上,这时我们需要保存一个管理指针(智能指针std::auto_ptr,引用计数的指针,或是其他的析构删除),最好的预防堆溢出的方法是在第二阶段是抛出异常。分配堆空间简单的示例代码如下:
#include <memory>
void joe_user()
{
std::auto_ptr<Base> p(/*...somehow create a Derived object via new...*/);
p->init();
...
}
第二种方案是组合joe_user前两句到create函数里。如果你是用工厂(factory)模式,譬如虚构造,你可以这两行放在静态方法中调用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的功能,更重要的是可以在创建对象不用调用Init().
void joe_user()
{
Base::Ptr p = Base::create();
...
}
我们在这个方法中,我们应当竭力避免调用Init(),那么就应该使派生类的构造、拷贝构造成为priviate或protected;
最后的方法,则和上面的都不同,在第二层类结构中加入foo()和bar(). 如果这两个函数要存取Derived的数据时,这个方法是不能用的。
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类也要删除init(),Base类和Derived类要删除foo() and bar(),在Base类构造中加入Helper的引用
class Base {
public:
Base(const Helper& h);
... // remove
... // remove
};
class Derived : public Base {
public:
... // remove
};
当我们定义Base::Base(const Helper&)时,它会正确调用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的派生类的构造中,但不是任何的数据都可以传至Helper派生类。比如Helper::foo()和 Helper::bar() 就不能存取数据在这个类中,特别是数据是Derived类中中声明的数据。
Helper派生类也可以做成类似joe_user功能,例如:
Derived::Derived(const Helper& h)
: Base(h)
{
...
}
如果Helper不需要数据,那么可以通过一个静态方法来替代它。
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
{
...
}