C ++ множественное наследование с интерфейсами? - PullRequest
6 голосов
/ 11 июня 2010

Приветствую всех,

Я пришел из 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_ */

Myпроизводный класс.

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 ]

7 голосов
/ 11 июня 2010

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

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

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

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

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

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

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

4 голосов
/ 11 июня 2010

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

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

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

    virtual void init() = 0;
};
3 голосов
/ 11 июня 2010

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

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

#include "iviewer.h"

IViewer::~IViewer() { }

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

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

3 голосов
/ 11 июня 2010

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

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

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

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

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

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

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

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

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

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

2 голосов
/ 23 мая 2013

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

2 голосов
/ 11 июня 2010

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

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

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

...