虚拟继承的一些巧妙使用可以允许 CloneableMixin::clone 覆盖 Base::clone 吗?
你CloneableMixin<Base,Derived>
不能覆盖任何方法Base
——无论是多态的还是隐藏的——因为CloneableMixin<Base,Derived>
不是从Base
.
另一方面,如果CloneableMixin<Base,Derived>
是从Base
你那里派生的,就不再需要它是一个mixin,因为 -
class Derived : public CloneableMixin<Base,Derived> {....};
会继承Base
.
因此,对于您的示例的需要,此处说明的解决方案就足够了:
#include <iostream>
// cloner v1.0
template <class Base, class Derived>
struct cloner : Base
{
Base *clone() const override {
return new Derived( dynamic_cast<const Derived &>(*this) );
}
~cloner() override {};
};
struct Base
{
virtual Base * clone() const = 0;
Base() {
std::cout << "Base()" << std::endl;
}
virtual ~Base() {
std::cout << "~Base()" << std::endl;
}
};
struct A : cloner<Base,A>
{
A() {
std::cout << "A()" << std::endl;
}
~A() override {
std::cout << "~A()" << std::endl;
}
};
int main()
{
A a;
Base * pb = a.clone();
delete pb;
}
(如果您正在编译为 C++03 标准而不是 C++11,那么您可以简单地删除override
关键字的出现。)
该解决方案将分解为一些更真实的类层次结构,例如在模板方法模式的插图中:
#include <iostream>
#include <memory>
using namespace std;
// cloner v1.0
template<class B, class D>
struct cloner : B
{
B *clone() const override {
return new D(dynamic_cast<D const&>(*this));
}
~cloner() override {}
};
/* Abstract base class `abstract` keeps the state for all derivatives
and has some pure virtual methods. It has some non-default
constructors.
*/
struct abstract
{
virtual ~abstract() {
cout << "~abstract()" << endl;
}
int get_state() const {
return _state;
}
void run() {
cout << "abstract::run()" << endl;
a_root_method();
another_root_method();
}
virtual void a_root_method() = 0;
virtual void another_root_method() = 0;
virtual abstract * clone() const = 0;
protected:
abstract()
: _state(0) {
cout << "abstract(): state = " << get_state() << endl;
}
explicit abstract(int state) : _state(state) {
cout << "abstract(" << state << ") : state = "
<< get_state() << endl;
}
int _state;
};
/* Concrete class `concrete` inherits `abstract`
and implements the pure virtual methods.
It echoes the constructors of `abstract`. Since `concrete`
is concrete, it requires cloneability.
*/
struct concrete : cloner<abstract,concrete>
{
concrete() {
cout << "concrete(): state = " << get_state() << endl;
}
explicit concrete(int state) : abstract(state) { //<- Barf!
cout << "concrete(" << state << ") : state = "
<< get_state() << endl;
}
~concrete() override {
cout << "~concrete()" << endl;
}
void a_root_method() override {
++_state;
cout << "concrete::a_root_method() : state = "
<< get_state() << endl;
}
void another_root_method() override {
--_state;
cout << "concrete::another_root_method() : state = "
<< get_state() << endl;
}
};
int main(int argc, char **argv)
{
concrete c1;
unique_ptr<abstract> pr(new concrete(c1));
pr->a_root_method();
pr->another_root_method();
unique_ptr<abstract> pr1(pr->clone());
pr1->a_root_method();
return 0;
}
当我们尝试构建它时,编译器将在(在注释处)abstract(state)
的构造函数的初始化时给出错误
,说:concrete
Barf!
error: type 'abstract' is not a direct or virtual base of 'concrete'
或大意的话。确实,concrete
is not abstract
but的直接基数cloner<abstract,concrete>
。但是,我们不能将构造函数重写为:
/*Plan B*/ explicit concrete(int state) : cloner<abstract,concrete>(state){....}
因为没有这样的构造函数
cloner<abstract,concrete>::cloner<abstract,concrete>(int)
但编译器的诊断建议修复。这是虚拟继承可以提供帮助的。我们需要abstract
成为 的虚拟基地concrete
,这实际上意味着“荣誉的直接基地concrete
”,我们可以通过建立B
虚拟基地来实现cloner<B,D>
:
// cloner v1.1
template<class B, class D>
struct cloner : virtual B
{
B *clone() const override {
return new D(dynamic_cast<D const&>(*this));
}
~cloner() override {}
};
这样,我们就有了一个干净的构建和输出:
abstract(): state = 0
concrete(): state = 0
concrete::a_root_method() : state = 1
concrete::another_root_method() : state = 0
concrete::a_root_method() : state = 1
~concrete()
~abstract()
~concrete()
~abstract()
~concrete()
~abstract()
原则上有充分的理由对虚拟继承保持警惕,并至少将其用于具有架构原理的情况——而不是像我们刚才使用的那样用于变通方法。
如果我们更喜欢不使用虚拟继承来解决这个问题,那么我们必须以某种方式确保有一个构造函数与, 的任意构造函数cloner<B,D>
相呼应。然后任何相应的构造函数将能够初始化它的直接基
,无论参数是什么。B
B
D
cloner<B,D>
这是 C++03 的梦想,但借助 C++11 中可变参数模板参数的魔力,这很容易:
// cloner v1.2
template<class B, class D>
struct cloner : B
{
B *clone() const override {
return new D(dynamic_cast<D const&>(*this));
}
~cloner() override {}
// "All purpose constructor"
template<typename... Args>
explicit cloner(Args... args)
: B(args...){}
};
有了这个,我们可以concrete
将构造函数重写为/*Plan B*/
,并且我们再次获得了正确的构建和可执行文件。