Множественное наследование 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 для IViewer' collect2: LD возвратил 1 выход статус

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

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++ не волнует, находится ли он в a .cpp файл или a .H-файл. В отличие от Java, препроцессор C/C++ сначала разрешает все includes и генерирует один файл, содержащий весь код. Затем он передает это компилятору C/C++. Вы можете фактически включить a .cpp, если хотите. Не очень хорошая идея.


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

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

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

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

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

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

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


Да, вы должны объявить init в своем PlaneViewer Как хорошо. Если нет, то ... --1--> не будет существовать в PlaneViewer и PlaneViewer по-прежнему будет считаться абстрактным (потому что нет реализации init).

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

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

    virtual void init() = 0;
};

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

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

#include "iviewer.h"

IViewer::~IViewer() { }

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

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


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

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

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


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

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

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

C++ предшествует Java и не утруждает себя определением специального ключевого слова "interface" для чистых виртуальных классов. Таким образом, вам фактически нужно выполнить некоторую работу, которую компилятор Java делает автоматически:

/ / эквивалент C++ класс!--3-->

class Numeric {
private:
   Numeric(const Numeric&);
   Numeric& operator=(const Numeric&);
public:
   Numeric() {}
   virtual ~Numeric() {}

   virtual int    toInteger() = 0;
   virtual double toDouble() = 0;
};

еще одно хорошее правило для C++: всякий раз, когда вы наследуете от базового класса с чистыми виртуальными методами, повторно объявляйте их в производном классе, даже если вы оставляете их как чистые виртуалы. Это не повредит производительности, и это позволяет всем знать, что объект является только частичной реализацией.