2010-06-11 1 views
6

Приветствия все,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 также, поскольку он уже определен i n IView?

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

PlaneViewer] + 0x28): неопределенная ссылка на `TypeInfo для IViewer» collect2: л.д. возвращается статус 1 выхода

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

+0

Могу ли я спросить о вашем дизайне? Зачем вам нужно связывать QWidget и IViewer в одной иерархии наследования? Какую проблему вы пытаетесь решить с помощью множественного наследования? Я прошу, потому что множественное наследование полезно в нескольких редких ситуациях, но часто проблемы лучше решаются по-другому. –

+0

В моем приложении есть несколько типов зрителей, которые используют одни и те же данные. (3D-данные вокселя) .Eg: 2D-зрители (плоскость XY, плоскость YZ, плоскость ZX) и 3D-просмотрщик. И в будущем theres станут еще более зрителями. QWiget должен использовать рисование и рендеринг данных. IView - абстрактный класс/интерфейс для объявления командных методов и данных для всех типов зрителей. –

ответ

3

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

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

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

Я не могу Скомпилируйте выше кода, дают ошибку: PlaneViewer] + 0x28): неопределенная ссылка на `TypeInfo для IViewer» collect2: л.д. возвращается 1 выход статус

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

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

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

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

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

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

7

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

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

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

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

PlaneViewer] + 0x28): не определено ссылка на `TypeInfo для IViewer» collect2: LD возвращается 1 выход статус

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

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

2

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

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

В качестве дополнительного примечания, you should provide virtual destructors with empty body for your interfaces.

4

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

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

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

    virtual void init() = 0; 
}; 
3

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

Вы должны скомпилировать и скомпоновать файл, который содержит:

#include "iviewer.h" 

IViewer::~IViewer() { } 

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

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

2

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