我曾经写过这样的接口类:
class SomeInterface
{
public:
virtual void doThings() = 0;
virtual void run() = 0;
};
class OtherInterface
{
public:
virtual void doStuff() = 0;
virtual void run() = 0;
};
这意味着我可以轻松地继承几个接口并像这样使用它
class ConcreteClass : public SomeInterface, public OtherInterface
{
public:
virtual void doThings() { std::cout << "do things" << std::endl; }
virtual void doStuff() { std::cout << "do stuff" << std::endl; }
virtual void run() { std::cout << "do run" << std::endl; }
};
int main()
{
ConcreteClass myObject;
myObject.run();
return 0;
}
但我最近读到了关于非虚拟界面的成语(像这样的东西http://www.gotw.ca/publications/mill18.htm),按照这些指导方针,我应该像这样编写我的界面:
class SomeInterface
{
public:
void doThings() { doThingsImplementation(); }
void run() { runImplementation(); }
private:
virtual void doThingsImplementation() = 0;
virtual void runImplementation() = 0;
};
class OtherInterface
{
public:
void doStuff() { doStuffImplementation(); }
void run() { runImplementation(); }
private:
virtual void doStuffImplementation() = 0;
virtual void runImplementation() = 0;
};
这意味着其余代码现在看起来像这样
class ConcreteClass : public SomeInterface, public OtherInterface
{
private:
virtual void doThingsImplementation() { cout << "do things" << endl; }
virtual void doStuffImplementation() { cout << "do stuff" << endl; }
virtual void runImplementation() { cout << "run" << endl; }
};
int main()
{
ConcreteClass myObject;
myObject.run();
return 0;
}
...除了现在它没有编译,因为对的调用run()
变得模棱两可(error: request for member ‘run’ is ambiguous
)。过去接口中无害的重叠现在会导致名称冲突。
重读非虚拟接口习语的论点,我觉得问题在于我将它应用于纯虚函数的虚函数。是这样吗?我应该只将 NVI 成语用于非纯虚函数吗?
编辑1:有人说该run()
函数可能在它自己的接口类中。这实际上很有意义:要么两者run()
具有相同的含义并且应该考虑接口,要么它们具有不同的含义并且我们希望编译器说事情是模棱两可的。当使用纯虚函数不存在的 NVI 时,这仍然给我们留下了菱形继承问题
编辑2:事实证明,如果我在其自己的接口类中将其分解为纯虚函数,并run()
从中继承,事情实际上并没有我想象的那么好。如果我引入(从两个接口类继承)并从中继承,我可以调用但我不能从 a 多态地使用它(同样,调用被认为是模棱两可的)。SomeInterface
OtherInterface
CombinedInterface
ConcreteClass
run()
myObject
CombinedInterface*
结论:我的结论是 1) 接口不应该重叠,或者被分解。2)接口类之间的继承应该总是虚拟的