C ++ Множественное наследование с интерфейсами?

Привет всем,

У меня опыт работы с 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. Нужно ли также объявить метод init () в интерфейсе PlaneViewer, потому что он уже определен в IView?

2. Я не могу выполнить приведенный выше код, выдаю ошибку:

PlaneViewer] + 0x28): неопределенная ссылка на `typeinfo for IViewer 'collect2: ld вернула 1 статус выхода

Должен ли я иметь реализацию IView в файле CPP (потому что все, что мне нужно, это интерфейс, а не реализация)?


person Ashika Umanga Umagiliya    schedule 11.06.2010    source источник
comment
Могу я спросить о вашем дизайне? Зачем вам нужно связывать QWidget и IViewer в одной иерархии наследования? Какую проблему вы пытаетесь решить с помощью множественного наследования? Я спрашиваю, потому что множественное наследование полезно в некоторых редких ситуациях, но часто проблемы лучше решать другим способом.   -  person CB Bailey    schedule 11.06.2010
comment
В моем приложении есть несколько типов программ просмотра, которые используют одни и те же данные (данные трехмерных вокселей). Например: средства просмотра двухмерных изображений (плоскость XY, плоскость YZ, плоскость ZX) и средство трехмерного просмотра. И в будущем будет еще несколько программ просмотра. QWiget использует рисование и рендеринг данных. IView - это абстрактный класс / интерфейс для объявления методов и данных команд для всех типов зрителей.   -  person Ashika Umanga Umagiliya    schedule 11.06.2010


Ответы (6)


Нужно ли также объявить метод init () в интерфейсе PlaneViewer, потому что он уже определен в IView?

Вам не нужно объявлять init () в PlaneViewer, но если вы этого не сделаете, PlaneViewer будет абстрактным классом, что означает, что вы не можете создать его экземпляр.

Если вы хотите спросить, нужна ли вам 'void init ();' в файле заголовка для PlaneViewer и в файле .cpp. Ответ положительный.

Я не могу выполнить приведенный выше код, выдает ошибку: PlaneViewer] + 0x28): неопределенная ссылка на `typeinfo for IViewer 'collect2: ld вернула 1 статус выхода

Я думаю, что либо вы создаете другой код, либо ваша команда компиляции неверна.

Я удалил материал QT и смог нормально построить ваш код с g ++.

Ошибка означает, что класс IViewer не был найден компоновщиком.

Я получаю эту ошибку, если удаляю часть '= 0', которая делает IViewer :: init () чистой виртуальной функцией. Вы также можете получить эту ошибку, если раскомментировали конструктор и / или деструктор в IViewer.

Должен ли я иметь реализацию IView в файле CPP?

Нет. C ++ не волнует, находится ли он в файле .cpp или .h файле. В отличие от Java, препроцессор C / C ++ сначала разрешает все включения и генерирует один файл, содержащий весь код. Затем он передает это компилятору C / C ++. Вы можете включить .cpp, если хотите. Но это не очень хорошая идея.

person jcoffland    schedule 11.06.2010

Хороший способ подумать о классах интерфейса - это указать, какие методы, производные классы, ДОЛЖНЫ реализовывать.

Нужно ли также объявить метод init () в интерфейсе PlaneViewer, потому что он уже определен в IView?

Быстрый ответ: да, вы должны реализовать метод init в IViewer, потому что в базовом классе метод объявлен как чисто виртуальный. Это означает, что любой производный класс ДОЛЖЕН предоставлять свою собственную реализацию этого метода, поскольку не реализован метод базового класса.

2. Я не могу выполнить приведенный выше код, выдаю ошибку:

PlaneViewer] + 0x28): неопределенная ссылка на `typeinfo for IViewer 'collect2: ld вернула 1 статус выхода

Это ошибка компилятора g ++, которая указывает (как указано выше), что у вас есть производный класс из базы, которая имеет чистую виртуальную функцию, и что производный класс не реализует чистый виртуальный метод, , поскольку он должен .

Да, и следует также отметить, что у вас нет проблемы с множественным наследованием, проблема все равно существовала бы, если бы были задействованы только IViewer и PlaneViewer.

person radman    schedule 11.06.2010

Да, вы также должны указать init в своем PlaneViewer. Если бы вы этого не сделали, то init не существовало бы в PlaneViewer, а PlaneViewer все равно считался бы абстрактным (потому что не было реализации init).

Вам нужно определить пустые тела для вашего (виртуального) деструктора в IViewer. «Интерфейсы» в C ++ на самом деле не являются интерфейсами, только по соглашению вы создаете класс со всеми чисто виртуальными методами и без полей: тем не менее, с точки зрения компилятора, они по-прежнему просто «обычные» классы, так что вы по-прежнему необходимо предоставить реализацию деструктора.

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

    virtual void init() = 0;
};
person Dean Harding    schedule 11.06.2010

Проблема typeinfo вызвана отсутствием реализации деструктора для класса IViewer. Обычно компиляторы генерируют внутренние структуры данных (например, «typeinfo») вместе с виртуальным деструктором.

Вам необходимо скомпилировать и связать файл, содержащий:

#include "iviewer.h"

IViewer::~IViewer() { }

Использование виртуального деструктора является хорошей практикой, поскольку это дает компилятору модуль компиляции для использования информации RTTI, а также позволяет оператору удаления работать правильно при вызове указателя базового класса.

У других есть ответ на вопрос о методе init (), но вкратце: если вы собираетесь реализовать его в PlaneViewer, вам необходимо его объявить.

person janm    schedule 11.06.2010

Да, вам нужно повторно объявить virtual void init() в подклассе и реализовать его, потому что IViewer объявляет функцию чисто виртуальной.

См. другой вопрос для объяснения вашей ошибки. Это вызвано объявлением виртуальной функции (не чистой) без ее определения. Это не очевидно из опубликованного вами кода, поэтому я подозреваю, что у вас могут быть устаревшие объектные файлы, которые не были перестроены (вы закомментировали конструктор IViewer и виртуальный деструктор).

В качестве дополнительного примечания вы должны предоставить виртуальным деструкторам пустые body для ваших интерфейсов.

person Alex B    schedule 11.06.2010

Я проделал значительную работу над обоими языками, и есть шаблон резака для печенья, которому вы обычно можете следовать, чтобы превратить интерфейс Java в интерфейс C ++:

// Начинаем с интерфейса Java

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

C ++ предшествует Java и не требует определения специального ключевого слова interface для чистых виртуальных классов. Таким образом, вам фактически нужно проделать некоторую работу, которую компилятор 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 ++: всякий раз, когда вы наследуете от базового класса чистыми виртуальными методами, повторно объявляйте их в производном классе, даже если вы оставляете их как чистые виртуальные. Это не снижает производительности и позволяет всем знать, что объект является лишь частичной реализацией.

person Zack Yezek    schedule 23.05.2013