C ++带接口的多重继承?

时间:2010-06-11 05:38:29

标签: c++ inheritance virtual multiple-inheritance

问候所有人,

我来自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(){

}

我的问题是:

  1. 是否有必要在PlaneViewer接口中声明方法init(),因为它已经在IView中定义了?
  2. 2.我无法编译上面的代码,给出错误:

    PlaneViewer] + 0x28):对IViewer的'typeinfo'的未定义引用 collect2:ld返回1退出状态

    我是否必须在CPP文件中实现IView(因为我想要的只是一个接口而不是实现)?

6 个答案:

答案 0 :(得分:7)

考虑接口类的一个好方法是它们指定派生类必须实现的方法。

  

是否有必要声明方法   PlaneViewer接口中的init()也是,   因为它已经定义了   IVIEW?

快速回答是,您必须在IViewer中实现init方法,因为在基类中,该方法被声明为纯虚拟。这意味着任何派生类必须提供自己的方法实现,因为没有实现基类方法。

  

2.我无法编译上面的代码,给出错误:

     

PlaneViewer] + 0x28):未定义   对IViewer的'typeinfo'的引用   collect2:ld返回1退出状态

这是一个g ++编译器错误,它指示(如上所述)你有一个来自具有纯虚函数的基类的派生类,并且派生类没有实现纯虚方法,因为它必须

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

答案 1 :(得分:4)

是的,您还必须在init中声明PlaneViewer。如果您没有,那么initPlaneViewer将不存在,而PlaneViewer仍然会被视为抽象(因为init没有实现)。

您需要在IViewer中为(虚拟)析构函数定义空体。 C ++中的“接口”并不是真正的接口,只是按照惯例,您创建一个包含所有纯虚方法且没有字段的类:但是,从编译器的角度来看,它们仍然只是“常规”类,所以你仍然需要提供析构函数的实现。

class IViewer
{
public:
    IViewer() { }
    virtual ~IViewer() { }

    virtual void init() = 0;
};

答案 2 :(得分:3)

  

是否有必要在PlaneViewer接口中声明方法init(),因为它已经在IView中定义了?

您不必在PlaneViewer中声明init(),但如果不这样,PlaneViewer将是一个抽象类,这意味着您无法实例化它。

如果你的意思是问你是否必须'void init();'在PlaneViewer的头文件和.cpp文件中。答案是肯定的。

  

我无法编译上面的代码,给出错误:   PlaneViewer] + 0x28):对IViewer的'typeinfo'的未定义引用'collect2:ld返回1退出状态

我认为你要么没有构建相同的代码,要么你的编译命令不正确。

我删除了QT的东西,并且能够使用g ++构建你的代码。

错误意味着链接器找不到IViewer类。

如果我删除使'IViewer :: init()'成为纯虚函数的'= 0'部分,我会收到该错误。如果在IViewer中取消注释构造函数和/或析构函数,也可能会出现该错误。

  

我是否必须在CPP文件中实现IView?

没有。 C ++不关心它是在.cpp文件还是.h文件中。与Java不同,C / C ++预处理器首先解析所有包含并生成包含所有代码的文件。然后它将它传递给C / C ++编译器。如果需要,您实际上可以包含.cpp。不过不是一个好主意。

答案 3 :(得分:3)

typeinfo问题是由于没有为IViewer类实现析构函数引起的。通常,编译器将生成内部数据结构(例如“typeinfo”)以及虚拟析构函数。

您需要编译并链接包含以下内容的文件:

#include "iviewer.h"

IViewer::~IViewer() { }

最好有一个虚拟析构函数,因为这为编译器提供了一个使用RTTI信息的编译单元,它还允许delete操作符在基类指针上调用时正常工作。

其他人已回答关于init()方法的问题,但总结如下:如果要在PlaneViewer中实现它,则需要声明它。

答案 4 :(得分:2)

是的,您需要在子类中重新声明virtual void init()并实现它,因为IViewer将该函数声明为纯虚函数。

有关错误的说明,请参阅another question。它是由声明虚函数(不纯)而不是定义它引起的。从您发布的代码中看不出来,所以我怀疑您可能有未重建的过时目标文件(您已注释掉IViewer构造函数和虚拟析构函数)。

另外请注意you should provide virtual destructors with empty body for your interfaces

答案 5 :(得分:2)

我已经完成了两种语言的重要工作,并且通常可以使用cookie切割器模式将Java接口转换为c ++接口:

//从Java接口开始

interface Numeric {
   public int     toInteger();
   public double  toDouble();
};

C ++早于Java,并且不需要定义特殊的&#34;接口&#34;纯虚拟类的关键字。因此,您必须有效地完成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 ++中遵循的另一条好规则是,无论何时从具有纯虚方法的基类继承,即使将它们保留为纯虚拟,也要在派生类中重新声明它们。它不会损害性能,它让每个人都知道该对象只是部分实现。