在类层次结构的设计中,我使用的是一个抽象基类,它声明派生类将实现的各种方法。从某种意义上说,基类与C ++中的接口一样接近。但是,有一个具体问题。考虑下面的代码声明我们的接口类:
class Interface {
public:
virtual Interface method() = 0;
};
class Implementation : public Interface {
public:
virtual Implementation method() { /* ... */ }
};
当然,这不会编译,因为你不能在C ++中返回一个抽象类。为了解决这个问题,我使用以下解决方案:
template <class T>
class Interface {
public:
virtual T method() = 0;
};
class Implementation : public Interface<Implementation> {
public:
virtual Implementation method() { /* ... */ }
};
这个解决方案很有效,但对我来说,它看起来并不优雅,因为文本的冗余位是接口的参数。如果你们能用这个设计指出我们的任何其他技术问题,我会很高兴,但这是我唯一关注的问题。
有没有办法摆脱冗余模板参数?可能使用宏?
注意:有问题的方法必须返回一个实例。我知道如果method()
返回指针或引用,则没有问题。
答案 0 :(得分:5)
Interface::method()
将无法返回Interface
实例。返回非指针,非引用Interface
实例需要实例化Interface
本身的实例,这是非法的,因为Interface
是抽象的。如果希望基类返回对象实例,则必须使用以下之一:
指针:
class Interface
{
public:
virtual Interface* method() = 0;
};
class Implementation : public Interface
{
public:
virtual Interface* method() { /* ... */ }
};
参考:
class Interface
{
public:
virtual Interface& method() = 0;
};
class Implementation : public Interface
{
public:
virtual Interface& method() { /* ... */ }
};
模板参数:
template<type T>
class Interface
{
public:
virtual T method() = 0;
};
class Implementation : public Interface<Implementation>
{
public:
virtual Implementation method() { /* ... */ }
};
答案 1 :(得分:5)
虽然由于显而易见的原因你无法通过 value 返回,但返回指针或引用完全没问题 - 这称为“协变返回类型”,它是一种有效的虚函数形式压倒一切的:
struct Base { virtual Base * foo(); }
struct Derived : Base { virtual Derived * foo(); }
关键是Derived::foo()
是真正的覆盖,而不是基础隐藏重载,因为Derived*
是指向派生类{{1}的指针}}。这同样适用于参考文献。
换句话说,如果你有一个Base
,并且你致电Base * p
,你可以随时将结果视为指向p->foo()
的指针(但是如果您有其他信息,例如因为你的班级实际上是Base
,那么你可以使用那些信息。)
相反的组合顺序,即“逆变参数类型”,不允许作为C ++的一部分。