2016-03-21 24 views
0

我有几个类d与以下形式的公共部分:如何与一个协变参数来覆盖的函数(在一个抽象基类)

class D 
{ 
    public: 
     D& foo(); 
     void bar(D&); 
} 

我想建立一个单一的抽象类从该他们都从中得到。

我的(幼稚)的尝试是:

// in .h file 
class B 
{ 
    public: 
     virtual B& foo() = 0; 
     virtual void bar(B&) = 0; 
} 

class D : public B 
{ 
    public: 
     D& foo() override; 
     void bar(D&) override; 
} 

// in .cpp file 
D& D::bar() {return *(new D());} 
void D::foo(D& d) {} 

这未能编译(我最终意识到是)一个相当合理的理由:任何功能覆盖

void bar(B&)=0; 

必须定义函数用于任何参考类型B的参数。所提供的候选

virtual void bar(D&) override; 

仅针对参考类型D的参数(较小的集合)定义。

请注意,这对函数foo不是问题。事实上,如果你用条注释掉这三行,所有的东西都编译得很好。

我认为这种现象的技术解释是C++不支持参数的协方差(但它确实支持参数中的协变性)。

答案后C++ covariance in parameters表明,我不能为我的类定义的接口(即抽象类)D.

有一些简单的或常规的方法来创建所有单“界面”我的班D?或者,也许隐藏这些类的不同实现有不同的设计模式。

在此先感谢您的意见和建议。

+2

没有什么像'协变参数'。返回类型可能是协变的。 –

+0

必须在派生类中使用在基类中声明的***精确***签名来实现纯虚拟成员函数。 –

+1

@πάνταῥεῖ允许协变返回类型。 – NathanOliver

回答

0

你不能,而且有很好的理由。派生类不能添加比它所衍生的接口更严格的先决条件,而不会破坏存在的所有OOP原则。通过要求参数在您的界面实现中更具体,这正是您正在做的。

的论点可以作出这样的事情可能是有用的:

struct IfaceA {}; 
struct IfaceB : IfaceA {}; 

struct Base { void f(IfaceB &); }; 
struct Derived : Base { void f(IfaceA &); }; 

这减少的前提条件,而不是增加他们,所以没关系。这只是在C++或其他任何我知道的语言中完成的,所以你不能这样做。

在这两种情况下,您都可以使用替代参数类型使过载并调用覆盖的版本。

这与返回类型相反。返回值是后置条件。您可以使发布条件更加具体,但不能使其更加广泛。所以你可以返回你的派生类型,但不能通过返回更抽象的类型来扩展它。尽管至少有一个非常常用的编译器非常糟糕,但C++实现了协变返回,所以存在大量的错误。

0

根据您提供的代码尝试覆盖两个不同的函数签名。 您拥有的最佳选项是使用相同的签名,然后投射结果。 一个简单的例子,

// in .h file 
class B 
{ 
    public: 
     virtual B* foo() = 0; 
     virtual void bar(B*) = 0; 
}; 

class D : public B 
{ 
    public: 
     B* foo() override; 
     void bar(B*) override; 
}; 

// in .cpp file 
B* D::foo() 
{ 
    D* p=new D(); 
    return p; 
} 
void D::bar(B* d) 
{ 
    D* casted=dynamic_cast<D*>(d); 
} 


int main(void) 
{ 
    D son; 
    B* father=dynamic_cast<B*>(son.foo()); 
} 

我希望这能解决你的问题,或者至少给你一个线索。

+0

这似乎很有用,但我总是对使用dynamic_casts的智慧有点怀疑。我想有些情况下演员阵容是合适的,但我通常会尽量避免他们。你确定这个解决方案是否符合最近发布的C++指南? – sitiposit

+0

我也反对dynamic_cast,检查虚拟表的成本太高,我知道你在做什么,你应该总是使用static_cast来代替。关于C++的指导方针,我不得不承认,我已经研究了c/C++的老式方法,但是,我可以给我任何建议/来源,我会很感激。 –

0

你可以使用模板化的基类吗?

template <typename Deriving> 
struct Base { 
    virtual Deriving& foo() = 0; 
    virtual void bar(Deriving&) = 0; 
}; 

struct Deriving : public Base<Deriving> { 
    ... 
}; 

您没有单个接口类,但有一个接口模板,它有时候通用性不够。

+0

这看起来就像我在找的东西。我提出的问题看起来很自然。这个解决方案是解决问题的标准/常用方法吗?我正在努力保持良好的编码习惯并避免“黑客行为”。 – sitiposit

+0

这被称为“奇怪的循环模板模式”。这是一个相当常见的模式,不应该引起任何关注。 – joelw