我有几个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?或者,可能存在用于隐藏这些类的不同实现的不同设计模式。
提前感谢您的意见和建议。
丹
答案 0 :(得分:0)
你不能,而且有充分的理由。派生类不能添加比其派生的接口更具限制性的前置条件,而不会破坏存在的OOP的每个原则。通过要求参数在接口实现中更具体,这正是您正在做的事情。
可以说这样的事情可能有用:
struct IfaceA {};
struct IfaceB : IfaceA {};
struct Base { void f(IfaceB &); };
struct Derived : Base { void f(IfaceA &); };
这减少了先决条件而不是增加它们,所以没关系。它完全没有用C ++或我知道的任何其他语言来完成,所以你不能这样做。
在这两种情况下,您都可以使用替代参数类型进行重载并调用重写版本。
与返回类型相反的情况。返回值是后置条件。您可以使帖子条件更具体,但不能使它们更广泛。因此,您可以返回派生类型,但不能通过返回更抽象的类型来扩展它。 C ++实现协变返回,但至少有一个非常常用的编译器非常糟糕,因此存在大量错误。
答案 1 :(得分: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());
}
我希望这可以解决您的问题,或者至少为您提供线索。
答案 2 :(得分:0)
你能使用模板化的基类吗?
template <typename Deriving>
struct Base {
virtual Deriving& foo() = 0;
virtual void bar(Deriving&) = 0;
};
struct Deriving : public Base<Deriving> {
...
};
您没有单一的接口类,但只有一个接口模板,有时候它足够通用。