覆盖所需的反差方法

时间:2011-08-22 08:38:05

标签: c++ override virtual covariance contravariance

我很难找到(我确信这是一种非常常见的)设计模式来解决以下问题。考虑一下这段代码:

class AA {};
class BB : public AA {};

class A
{
public:
    virtual void foo(AA& aa) = 0;
};

class B : A
{
public:
    void foo(BB& bb){cout<<"B::foo"<<endl;}
};

int main()
{
    B b;
    BB bb;
    b.foo(bb);
}

此代码无法编译,因为B类不会覆盖纯虚函数'foo'。编译器认为B声明的foo仅作为foo的重载,因为覆盖函数中的输入参数中不允许使用协方差。

现在,我理解这个的原因。 B继承自A的事实意味着它应该能够使用类型AA的参数处理对foo的任何调用,并且前面的代码没有给出任何实现来处理除BB之外的任何参数类型。

当然我可以在B的foo实现中将aa转换为BB,但我正在寻找一种保留类型安全的解决方案,并且实际上迫使B类的实现者也实现了一个从AA继承的类用于编译的代码。在一个理想的世界里,我可以编写一些看起来像这个伪代码的东西:

class A
{
public:
    abstract class AA{}; //indicates that any child of A must implement also child of AA
    abstract void foo(AA& aa);
};

class B : public A
{
public:
    class BB : AA{}; //will not compile without this
    void foo(BB& bb){cout<<"B::foo"<<endl;}
};

有没有办法在C ++中实现类似的功能? (可能通过某种映射对象来增强,而不需要继承)

请注意,实际上(与示例不同),BB和AA之间的继承是至关重要的,因为AA有许多孩子具有许多品质,最后我想要完成的是迭代A类的向量并使用适当的参数(AA的矢量)运行'foo'

2 个答案:

答案 0 :(得分:8)

要提供类型安全性,您应该使用模板而不是继承。

class AA {};
class BB : AA {};

template <typename Managed> class FooManager {
    virtual void foo(Managed& m) { /* default implementation */ }
};

class B : public FooManager<BB> {
    void foo(BB bb) { cout << "B:foo()" << endl; }
};

稍后在代码中,例如,如果要遍历数组,

template<typename Managed> void processAll(vector<Managed> v, FooManager<Managed> mgr) {
    for(Managed& m : v) mgr.foo(m);
}

B b;
vector<BB> bbs;
processAll(bbs, b);

编辑:拼写错误修复

答案 1 :(得分:3)

你可以看一下访客模式。这是“双重调度”类型问题的一般解决方案(基于对象消息调度到虚拟函数)。

即,将foo()放入访问者,并将A::foo()重命名为A::Visit(FooVisitor& )

编辑:澄清一下,它可能有助于解开层次结构的目的。如果你考虑一下,你试图用另一个(AABB)来模拟一个层次结构(AB)的关系。这对于建模,甚至从概念上思考都非常尴尬。

要将其重新构建为访问者,您通常会将其中一个层次结构转换为单个类,而是通过访问者对可以在算法层次结构中对该类执行的操作进行建模。这更加健壮,因为它会强制您显式实现层次结构关系的每个组合,并且如果稍后修改层次结构,它将在编译时中断(好)。

class A; class B;
struct AVisitor 
{ 
    virtual ~AVisitor() { } 

    virtual void Visit(A& ) = 0;
    virtual void Visit(B& ) = 0;
};

class A
{
public:
    virtual ~A() { }

    virtual void Visit(AVisitor & visitor) { visitor.Visit(*this); }
};

class B : public A
{
public:
    virtual void Visit(AVisitor & visitor) { visitor.Visit(*this); }
};

struct PrintingVisitor : public AVisitor
{
    void Visit(A& a){cout<<"A::foo"<<endl;}
    void Visit(B& b){cout<<"B::foo"<<endl;}
};

int main()
{
    B b;
    PrintingVisitor printer;
    b.Visit(printer);
}