带有接口的 C++ 多重继承?
Posted
技术标签:
【中文标题】带有接口的 C++ 多重继承?【英文标题】:C++ Multiple inheritance with interfaces? 【发布时间】:2011-03-02 12:22:55 【问题描述】:大家好,
我来自 Java 背景,在多重继承方面遇到困难。
我有一个名为 IView 的接口,它具有 init() 方法。我想派生一个名为 PlaneViewer 的新类,实现上述接口并扩展另一个类。 (QWidget)。
我的实现如下:
IViwer.h(只有头文件,没有 CPP 文件):
#ifndef IVIEWER_H_
#define IVIEWER_H_
class IViewer
public:
//IViewer();
///virtual
//~IViewer();
virtual void init()=0;
;
#endif /* IVIEWER_H_ */
我的派生类。
PlaneViewer.h
#ifndef PLANEVIEWER_H
#define PLANEVIEWER_H
#include <QtGui/QWidget>
#include "ui_planeviewer.h"
#include "IViewer.h"
class PlaneViewer : public QWidget , public IViewer
Q_OBJECT
public:
PlaneViewer(QWidget *parent = 0);
~PlaneViewer();
void init(); //do I have to define here also ?
private:
Ui::PlaneViewerClass ui;
;
#endif // PLANEVIEWER_H
PlaneViewer.cpp
#include "planeviewer.h"
PlaneViewer::PlaneViewer(QWidget *parent)
: QWidget(parent)
ui.setupUi(this);
PlaneViewer::~PlaneViewer()
void PlaneViewer::init()
我的问题是:
-
是否需要在 PlaneViewer 接口中声明 init() 方法,因为它已经在 IView 中定义?
2.我无法编译上面的代码,给出错误:
PlaneViewer]+0x28): 未定义对 `typeinfo for IViewer' 的引用 collect2: ld 返回 1 个退出状态
我是否必须在 CPP 文件中实现 IView(因为我想要的只是一个接口,而不是实现)?
【问题讨论】:
我能问一下你们的设计吗?为什么需要在同一个继承层次中将 QWidget 和 IViewer 绑定在一起?你想用多重继承解决什么问题?我问是因为多重继承在一些罕见的情况下很有用,但问题通常以不同的方式得到更好的解决。 在我的应用程序中,有几种类型的查看器共享相同的数据。(3D 体素数据)。例如:2D 查看器(XY 平面、YZ 平面、ZX 平面)和 3D 查看器。未来还有会有更多的观众。 QWiget是使用Data的绘制和渲染。 IView 是一个抽象类/接口,用于为所有类型的查看器声明命令方法和数据。 【参考方案1】:考虑接口类的一个好方法是它们指定派生类必须实现的方法。
是否需要声明方法 PlaneViewer 界面中的 init() , 因为它已经定义在 爱视?
快速回答是,是的,您必须在IViewer
中实现init 方法,因为在基类中该方法被声明为纯虚拟方法。这意味着任何派生类都必须提供自己的该方法实现,因为没有实现基类方法。
2.我无法编译上面的代码,给出错误:
PlaneViewer]+0x28): 未定义 参考“IViewer 的类型信息” collect2: ld 返回 1 个退出状态
这是一个 g++ 编译器错误,表明(如上所述)您从具有纯虚函数的基类派生了一个派生类,并且派生类没有实现纯虚方法,因为它必须。
哦,还应该注意的是,你没有多重继承的问题,如果只涉及IViewer
和PlaneViewer
,问题仍然存在。
【讨论】:
【参考方案2】:是的,您还必须在您的 PlaneViewer
中声明 init
。如果你不这样做,那么init
将不存在于PlaneViewer
中并且PlaneViewer
仍将被视为抽象(因为没有init
的实现)。
您需要在IViewer
中为您的(虚拟)析构函数定义空体。 C++ 中的“接口”并不是真正的接口,只是按照惯例,您创建一个包含所有纯虚拟方法且没有字段的类:但是,从编译器的角度来看,它们仍然只是“常规”类,所以您仍然需要提供析构函数的实现。
class IViewer
public:
IViewer()
virtual ~IViewer()
virtual void init() = 0;
;
【讨论】:
【参考方案3】:是否需要在 PlaneViewer 接口中声明 init() 方法,因为它已经在 IView 中定义?
您不必在 PlaneViewer 中声明 init(),但如果您不声明,PlaneViewer 将是一个抽象类,这意味着您无法实例化它。
如果你想问你是否必须有 'void init();'在 PlaneViewer 的头文件和 .cpp 文件中。答案是肯定的。
我无法编译上面的代码,给出错误: PlaneViewer]+0x28): undefined reference to `typeinfo for IViewer' collect2: ld returned 1 exit status
我认为要么您没有构建相同的代码,要么您的编译命令不正确。
我去掉了 QT 的东西,能够用 g++ 很好地构建你的代码。
该错误表示链接器未找到 IViewer 类。
如果我删除使 'IViewer::init()' 成为纯虚函数的 '=0' 部分,我会收到该错误。如果您取消注释 IViewer 中的构造函数和/或析构函数,也可能会出现该错误。
我必须在 CPP 文件中实现 IView 吗?
没有。 C++ 不关心它是在 .cpp 文件还是 .h 文件中。与 Java 不同,C/C++ 预处理器首先解析所有包含并生成一个包含所有代码的文件。然后它将它传递给 C/C++ 编译器。如果需要,您实际上可以包含 .cpp。不过这不是一个好主意。
【讨论】:
【参考方案4】:typeinfo 问题是由于没有实现 IViewer 类的析构函数引起的。通常编译器会生成内部数据结构(例如“typeinfo”)以及虚拟析构函数。
您需要编译并链接一个包含以下内容的文件:
#include "iviewer.h"
IViewer::~IViewer()
使用虚拟析构函数是一种很好的做法,因为它为编译器提供了一个编译单元来使用 RTTI 信息,并且它还允许删除运算符在基类指针上调用时正确工作。
其他人已经回答了关于init()方法的问题,但总结一下:如果你要在PlaneViewer中实现它,你需要声明它。
【讨论】:
【参考方案5】:是的,你需要在子类中重新声明virtual void init()
并实现它,因为IViewer
将函数声明为纯虚函数。
请参阅another question 了解您的错误的解释。它是由声明一个虚函数(不是纯的)而不是定义它引起的。从您发布的代码中看不出来,所以我怀疑您可能有未重建的陈旧目标文件(您已注释掉 IViewer
构造函数和虚拟析构函数)。
作为补充说明,you should provide virtual destructors with empty body for your interfaces。
【讨论】:
【参考方案6】:我在这两种语言中都做了很多工作,并且您通常可以遵循一种千篇一律的模式来将 Java 接口转换为 c++ 接口:
// 从 Java 接口开始
interface Numeric
public int toInteger();
public double toDouble();
;
C++ 早于 Java,并且不会为纯虚拟类定义特殊的“接口”关键字。所以你实际上必须做一些 Java 编译器自动完成的工作:
//等价的C++类
class Numeric
private:
Numeric(const Numeric&);
Numeric& operator=(const Numeric&);
public:
Numeric()
virtual ~Numeric()
virtual int toInteger() = 0;
virtual double toDouble() = 0;
;
在 C++ 中要遵循的另一个好规则是,每当您从具有纯虚方法的基类继承时,即使您将它们保留为纯虚方法,也要在派生类中重新声明它们。它不会影响性能,它让每个人都知道该对象只是部分实现。
【讨论】:
以上是关于带有接口的 C++ 多重继承?的主要内容,如果未能解决你的问题,请参考以下文章