带有接口的 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++ 编译器错误,表明(如上所述)您从具有纯虚函数的基类派生了一个派生类,并且派生类没有实现纯虚方法,因为它必须

哦,还应该注意的是,你没有多重继承的问题,如果只涉及IViewerPlaneViewer,问题仍然存在。

【讨论】:

【参考方案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++ 多重继承?的主要内容,如果未能解决你的问题,请参考以下文章

C++中的多重继承

C++ 多重继承的真实例子是啥? [关闭]

C# 应该有多重继承吗? [关闭]

适配器模式(C++)多重继承和组合实现

C ++如何从具有不同返回类型的接口多重继承?

C++ 多重继承