C++ 实现子类扩展子接口而不覆盖已经覆盖的方法
Posted
技术标签:
【中文标题】C++ 实现子类扩展子接口而不覆盖已经覆盖的方法【英文标题】:C++ Implement subclasses that extends subInterfaces without overriding already overriden methods 【发布时间】:2014-02-19 20:06:14 【问题描述】:我创建了在 C++ 中扩展其他接口的接口(抽象类),我尝试实现它们,但编译时出现错误。
以下是错误:
main.cpp: In function 'int main()':
main.cpp:36:38: error: cannot allocate an object of abstract type 'Subclass'
Subclass * subObj = new Subclass();
^
Subclass.h:13:7: note: because the following virtual functions are pure within 'Subclass':
class Subclass : SubInterface
^
SuperInterface.h:13:18: note: virtual void SuperInterface::doSomething()
virtual void doSomething()=0;
这是我的资料来源:
#include <iostream>
class SuperInterface
public:
virtual void doSomething() = 0;
protected:
int someValue;
;
class SubInterface : public SuperInterface
public:
virtual void doSomethingElseThatHasNothingToDoWithTheOtherMethod() = 0;
protected:
int anotherValue;
;
class Superclass : public SuperInterface
public:
Superclass()
virtual ~Superclass()
void doSomething() std::cout << "hello ***!";
;
class Subclass : public SubInterface
public:
Subclass()
virtual ~Subclass()
void doSomethingElseThatHasNothingToDoWithTheOtherMethod() std::cout << "goodbye ***!";
;
int main(void)
Superclass * superObj = new Superclass();
Subclass * subObj = new Subclass();
这就是我想要的:
我希望我的实现能够意识到,因此具有与已经覆盖的方法相同的行为(例如 subObj->doSomething()
方法无需再次实现即可工作)。谁能告诉我如果可能的话,我应该怎么做才能做到这一点?谢谢。
【问题讨论】:
只是用我的眼睛解析你的代码。看起来像 Subclass 将 doSomethingElse 声明为纯虚拟。这是一个错字,对吗? 公开派生,去掉Subclass::doSomethingElse声明中的=0,在Subclass中实现doSomething。好吧,您不必公开派生,但这可能是您想要的。默认情况下,类是私有继承的。我将结构用于纯接口。 这是一个错字,我的错,我重命名了另一种方法,以明确两种方法具有不同的行为。 @codah 当你说在子类中实现 doSomething 时,你是在要求我做我不想做的事情。我正在寻找一种替代方法,它不需要进一步实现方法。而且我也不想在 SubInterface 中实现它,因为我想保持接口和实现分离并且实现中的继承始终存在 好吧对不起,我误会了;收回。 【参考方案1】:不,你不能通过简单的继承来做你想做的事。 Subclass 绝不会继承或提供doSomething()
的实现,因此您不能随意调用subObj->doSomething()
。你必须遵守subInterface
的接口契约。
您可以从Superclass
和Subinterface
继承Subclass
,并将doSomething()
实现为一种代理Superclass::doSomething()
。您仍然需要一个实现,但您不必“重新实现”它。
【讨论】:
【参考方案2】:您收到错误是因为您正在尝试创建抽象类的对象。
由于这条线void doSomethingElse()=0;
,您的Subclass
是一个抽象类。
如果一个类有一个纯虚函数,那么它将是一个抽象类。不能创建抽象类的对象,只能有引用或指向它的指针。
为了摆脱错误,doSomethingElse
在Subclass
中的声明应该是
void doSomethingElse();
而不是void doSomethingElse()=0;
我也不明白为什么需要两个接口。您可以从SuperInterface
派生Subclass
,因为它与SubInterface
基本相同
【讨论】:
doSomethingElse() 重命名以明确 doSomething() 和 doSomethingElse() 完全没有共同点,我也可能有完全不同的行为和返回类型。 =0 是一个错字现已更正!【参考方案3】:说实话,我完全确定你的设计想要表达什么,但这里至少有两个技术错误:
1.) 您在所有情况下都使用 private 继承,因此您实际上根本不处理“接口”。公共继承是这样实现的:
class SubInterface : public SuperInterface
2.) 您将=0
用于您显然想要实现的功能。
这将修复编译器错误,但设计仍然存在问题。考虑到您在问题末尾给出的动机,我建议组合而不是(公共)继承。在 C++ 中,共享功能最好用组合来表达。简单来说,就是将常用的功能封装在一个单独的类中,并为其他类配备它的对象。
class CommonFunctionality
//...
public:
void doSomething();
void doSomethingElse();
;
class SuperClass
//...
private:
CommonFunctionality m_functionality;
;
class SubClass : public SuperClass
//...
private:
CommonFunctionality m_functionality;
;
事实上,也许您甚至需要为 CommonFunctionality 创建一个类。也许简单的独立函数就可以了。具有 Java 背景的程序员(并且您的代码看起来有点像)倾向于将过多的东西放入类中,而不是 C++ 中所必需的。
【讨论】:
这是一个错字,我的错,我重命名了另一种方法,以明确两种方法具有不同的行为。顺便说一下,在继承声明中添加“public”并不会改变错误【参考方案4】:您的“子类”类应覆盖 2 个纯虚方法,因此:
class Subclass : SubInterface
public:
Subclass();
virtual ~Subclass();
void doSomethingElse() override;
void doSomething() override;
;
不这样做或声明
void doSomethingElse()=0;
class 子类也变成抽象的,不能被实例化。你可以看看:http://www.parashift.com/c++-faq-lite/pure-virtual-fns.html
这就是我想要的:我希望我的实现能够被意识到 与已经覆盖的方法相同的行为(例如 subObj->doSomething() 方法无需实现即可工作 再次)。谁能告诉我我应该怎么做才能做到这一点,如果 甚至有可能!???谢谢。
--> 可能声明方法是虚拟的而不是纯虚拟的
【讨论】:
可以在方法声明中添加“覆盖”。 我知道为什么子类也变得抽象了,我的问题是:有没有办法,一种解决方法,不必再次覆盖已经覆盖的方法。【参考方案5】:有2个问题,编译器明确说明:
问题 1
Subclass
中的SubInterface::doSomethingElse()()
被声明为纯虚拟,不管您试图在源文件中定义它(我很确定,这是一种复制粘贴错误)。
class Subclass : SubInterface
public:
Subclass();
virtual ~Subclass();
void doSomethingElse() = 0; // still pure?
;
解决办法很明显:
class Subclass : SubInterface
public:
Subclass();
virtual ~Subclass();
virtual void doSomethingElse() override
;
(此处使用 C++11 override
说明符,因此编译器将检查覆盖的正确性;这不是强制性的)
问题 2
doSomething()
甚至没有被覆盖,SuperInterface
和 Subclass
都没有,所以它保持纯虚拟。尽管doSomething()
在Superclass
中被覆盖,但Subclass
不知道Superclass
的存在。
解决方案:在SuperInterface
、Subclass
或Subclass
的任何子代中覆盖doSomething()
(还没有)。例如覆盖Subclass
:
class Subclass : SubInterface
public:
Subclass();
virtual ~Subclass();
virtual void doSomething() override
virtual void doSomethingElse() override
;
其他问题:
您在没有可见性说明符的情况下继承,即privat
ely。使用public
继承,直到你真的需要别的东西:
class Derved : public Base ;
您的源文件具有 .c
扩展名,但包含 C++ 代码。如果您没有通过命令行参数显式声明编程语言,这可能会使一些编译器感到困惑。按照惯例,大多数程序员使用.cpp
扩展名,而大多数编译器将此类文件视为 C++ 源文件。
【讨论】:
更正我的代码。事实是我无意中删除了部分代码。我还做了一些修改,以使我希望更清楚的整个事情! @PaikuHan BTW,不建议您编辑您的第一个帖子修复问题。这给以后会阅读此主题的任何人增加了困惑。 .c 扩展名是一个错字,因为您可能已经注意到阅读错误代码,该代码给出了带有 cpp 扩展名的文件名。我的 IDE 无法识别覆盖说明符,并且在编译时出现错误,指出如您所说,它仅适用于 c++11。但是我对您的解决方案的最大问题是,即使您在超类中覆盖了 doSomething(),您仍然必须在子接口或子类中再次覆盖它。现在我想真正的问题是:真的没有其他方法可以做到...子类的多重继承可能??? 我知道肯定会有混乱,但唯一的其他解决方案是删除整个帖子并重新写一遍以上是关于C++ 实现子类扩展子接口而不覆盖已经覆盖的方法的主要内容,如果未能解决你的问题,请参考以下文章
在 Swift 3.1 中为 UIView 子类设置默认外观而不覆盖 initialize()