Ogu*_*guk 6 c++ inheritance virtual-inheritance
昨天,我遇到了这个问题:迫使不合格的名称成为依赖值最初,它似乎是一个与破坏的VC++行为有关的非常具体的问题,但在尝试解决它时,我偶然发现了虚拟继承的使用模式我没有之前遇到过(在我告诉你问题之后,我会在一秒钟内解释).我发现它很有趣,所以我在SO和google上寻找它,但我找不到任何东西.也许,我只是不知道它的正确名称("方法注入"是我的猜测之一),它实际上是众所周知的.这也是我对社区提问的一部分:这是一种常见的使用模式还是另一种已知范式的特例?您是否看到任何可以通过其他解决方案避免的问题/陷阱?
此模式可以解决的问题如下:假设您有一个Morph带有方法的类doWork().在其中doWork(),调用了几个函数,其实现应该由用户选择(这就是调用类的原因Morph).让我们称之为函数变色龙(因为Morph类最终不知道它们将是什么).实现这一目标的一种方法当然是使变色龙成为Morph类的虚方法,这样用户就可以派生出来Morph并覆盖所选择的方法.但是,如果期望用户使用不同的组合来选择不同变色龙的实现,该怎么办呢?然后,对于每个组合,必须定义新类.另外,如果有多个Morph类相同的函数应该是变色龙呢?用户如何重用已经实现的替换?
至于"必须定义多个类"的问题,立即模板跳进了一个人的脑海.用户不能通过将类作为定义所需实现的模板参数来选择他想要的变色龙实现吗?即类似的东西Morph<ReplaceAB>应该有效地取代变色龙,A()并且B()在doWork()某些实施中,留下可能的其他变色龙,例如C(),未触动过.使用C++ 11和可变参数模板,即使组合也不会成为问题:Morph<ReplaceAB, ReplaceC, WhateverMore...>嗯,确实,这种模式可以做什么(参见下面的解释):
#include <iostream>
using namespace std;
// list all chameleons, could also be make some of them
// pure virtual, so a custom implementation is *required*.
struct Chameleons
{
virtual void A() { cout << "Default A" << endl; }
virtual void B() { cout << "Default B" << endl; }
virtual void C() { cout << "Default C" << endl; }
};
// Chameleon implementations for A and B
struct ReplaceAB : virtual Chameleons
{
virtual void A() { cout << "Alternative A" << endl; }
virtual void B() { cout << "Alternative B" << endl; }
};
// Chameleon implementation for C
struct ReplaceC : virtual Chameleons
{
virtual void C() { cout << "Alternative C" << endl; }
};
// A(), B(), C() in this class are made chameleons just by
// inheriting virtually from Chameleons
template <typename... Replace>
struct Morph : virtual Chameleons, Replace...
{
void doWork() {
A();
B();
C();
cout << endl;
}
};
int main()
{
//default implementations
Morph<>().doWork();
//replace A and B
Morph<ReplaceAB>().doWork();
//replace C
Morph<ReplaceC>().doWork();
//replace A, B and C;
Morph<ReplaceAB,ReplaceC>().doWork();
}
Run Code Online (Sandbox Code Playgroud)
其输出如下:
Default A
Default B
Default C
Alternative A
Alternative B
Default C
Default A
Default B
Alternative C
Alternative A
Alternative B
Alternative C
Run Code Online (Sandbox Code Playgroud)
只看到这方面的工作方案,上述想法的问题其实并没有那么明显:不能Morph只从指定为模板参数的类派生的,所以变色龙A(),B()而C()只是从无论采取什么样Morph继承?这实际上是不可能的,因为对变色龙的调用不依赖于模板参数,并且在依赖的继承类中不会查找此类非依赖名称(如果需要,请尝试使用它).这意味着,我们必须以某种方式实现变色龙调用绑定到以后可以被所需实现替换的东西.
这就是虚拟继承的用武之地:通过让Morph继承Chameleons(非依赖于模板参数),非限定变色龙调用doWork()绑定到虚拟函数中Chameleons.因为Morph并且这些Replacement类实际上是从中继承的Chameleons,所以Chameleons在任何Morph对象中只会有一个对象,并且虚函数调用将在运行时调度到最派生类中的实现,我们通过模板化继承"偷偷进入" .因此,虽然不合格的变色龙名称在doWork()编译时无法解析为所需的实现(根据标准),但它们仍然可以通过虚拟基类的间接层调用.好笑,对吧?(除非你告诉我这样做更容易以不同的方式进行,或者模式广为人知.)
你的解决方案工作正常。虚拟继承避免了歧义错误。可变参数模板带来了优雅的实例化语法。而不是类似的东西:
class M1 : public ReplaceAB, ReplaceC {} i1;
i1.doWork();
Run Code Online (Sandbox Code Playgroud)
你只有一行:
Morph<ReplaceAB, ReplaceC>().doWork();
Run Code Online (Sandbox Code Playgroud)
从我的角度来看,所提出的模式并不常见。同时,这真的是新事物吗?嗯,有数百万行代码......有人使用类似东西的机会根本不是零。您很可能永远无法确定这一点。