2010-06-11 17 views
6

Saludos a todos,C++ ¿Herencia múltiple con interfaces?

Vengo del fondo de Java y estoy teniendo dificultades con la herencia múltiple.

Tengo una interfaz llamada IView que tiene el método init(). Quiero derivar una nueva clase llamada PlaneViewer que implementa la interfaz anterior y extender otra clase. (QWidget).

Mi aplicación es como:

IViwer.h (sólo archivo de cabecera, hay un archivo CPP):

#ifndef IVIEWER_H_ 
#define IVIEWER_H_ 

class IViewer 
{ 
public: 
    //IViewer(); 
    ///virtual 
    //~IViewer(); 
    virtual void init()=0; 
}; 

#endif /* IVIEWER_H_ */ 

Mi clase derivada.

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(){ 

} 

Mis preguntas son:

  1. ¿Es necesario declarar método init() en la interfaz PlaneViewer también, porque ya está definido n IView?

2.I no puede complie código anterior, dará error:

PlaneViewer] + 0x28): undefined reference to `typeinfo para iViewer' collect2: ld devolvió el código de salida 1

¿Tengo tener implementación para IView en el archivo CPP (porque todo lo que quiero es una interfaz, no como implementación)?

+0

¿Puedo preguntar acerca de su diseño? ¿Por qué necesita unir QWidget e IViewer en la misma jerarquía de herencia? ¿Qué problema estás tratando de resolver con herencia múltiple? Lo pregunto porque la herencia múltiple es útil en algunas situaciones raras, pero a menudo los problemas se resuelven mejor de una manera diferente. –

+0

En mi aplicación hay varios tipos de espectadores que comparten los mismos datos (datos voxel 3D) .Eg: visores 2D (plano XY, plano YZ, plano ZX) y un visor 3D. Y en el futuro habrá más espectadores. QWiget utilizará el dibujo y la representación de Datos. IView es una clase/interfaz abstracta para declarar métodos de comando y datos para todo tipo de espectadores. –

Respuesta

3

¿Es necesario declarar método init() en la interfaz PlaneViewer también, porque ya está definido en IView?

No necesita declarar init() en PlaneViewer, pero si no lo hace, PlaneViewer será una clase abstracta, lo que significa que no puede crear una instancia.

Si quiere preguntar si tiene que tener 'void init();' en el archivo de encabezado para PlaneViewer y en el archivo .cpp. La respuesta es sí.

No puedo complie código anterior, dará error: PlaneViewer] + 0x28): undefined reference to `typeinfo para iViewer' collect2: ld devolvió 1 Estado de salida

creo que cualquiera que no está construyendo el mismo código o su comando de compilación es incorrecto.

Eliminé las cosas de QT y pude construir tu código perfectamente con g ++.

El error significa que la clase IViewer no fue encontrada por el enlazador.

Me sale ese error si elimino la parte '= 0' que hace 'IViewer :: init()' una función virtual pura. También podría obtener ese error si descomentó el constructor y/o destructor en IViewer.

¿Debo tener implementación para IView en el archivo CPP?

No. C++ no importa si está en un archivo .cpp o en un archivo .h. A diferencia de Java, el preprocesador C/C++ primero resuelve todas las inclusiones y genera un archivo que contiene todo el código. Luego pasa esto al compilador C/C++. De hecho, puede incluir un .cpp si lo desea. No es una buena idea sin embargo.

7

Una buena forma de pensar en las clases de interfaz es que especifican qué métodos DEBERÁN implementar las clases derivadas.

¿Es necesario declarar método init() en la interfaz PlaneViewer también, porque ya está definido en IView?

La respuesta rápida es que sí debe implementar el método init en IViewer porque en la clase base el método se declara como puramente virtual. Esto significa que cualquier clase derivada DEBE proporcionar su propia implementación de ese método ya que no hay un método de clase base implementado.

2.No puedo complie encima de código, dar error:

PlaneViewer] + 0x28): indefinido referencia a `typeinfo para iViewer' collect2: ld volvió 1 Estado de salida

se trata de un error del compilador g ++ que indica (como se indicó anteriormente) que tiene una clase derivada de una base que tiene una función virtual pura y que la clase derivada no implementa el método virtual puro, , ya que debe.

Ah, y también debe tener en cuenta que no está teniendo un problema con la herencia múltiple, el problema todavía existiría si se tratara de IViewer y PlaneViewer.

2

Sí, necesita volver a declarar virtual void init() en la subclase e implementarlo, porque IViewer declara que la función es puramente virtual.

Consulte another question para la explicación de su error. Se produce al declarar una función virtual (no pura) y no definirla. No se desprende del código que publicaste, así que sospecho que puedes tener archivos obsoletos que no fueron reconstruidos (has comentado IViewer constructor y destructor virtual).

Como nota adicional, you should provide virtual destructors with empty body for your interfaces.

4

Sí, tiene que declarar init en su PlaneViewer también. Si no lo hizo, entonces init no existiría en PlaneViewer y PlaneViewer todavía se consideraría abstracto (porque no hay implementación de init).

Necesita definir cuerpos vacíos para su destructor (virtual) en IViewer. Las "interfaces" en C++ no son realmente interfaces, solo por convención se crea una clase con todos los métodos puros virtuales y sin campos: sin embargo, siguen siendo solo clases "regulares" desde el punto de vista del compilador, por lo que aún necesidad de proporcionar una implementación del destructor.

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

    virtual void init() = 0; 
}; 
3

El problema de typeinfo es causado por no tener una implementación de un destructor para la clase IViewer. Normalmente, los compiladores generarán estructuras internas de datos (por ejemplo, "typeinfo") junto con el destructor virtual.

necesita compilar y vincular un archivo que contiene:

#include "iviewer.h" 

IViewer::~IViewer() { } 

Es una buena práctica tener un destructor virtual, porque esto le da al compilador de una unidad de compilación a utilizar la información de RTTI, y también permite la eliminación operador para que funcione correctamente cuando se le solicite un puntero de clase base.

Otros han respondido la pregunta sobre el método init(), pero en resumen: si va a implementarlo en PlaneViewer, necesita declararlo.

2

que he hecho un trabajo significativo en ambos idiomas y no hay un patrón cortador de galletas por lo general puede seguir para convertir una interfaz Java en una interfaz de C++:

// Comienza con la interfaz Java

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

C++ es anterior a Java y no se molesta en definir la palabra clave especial "interfaz" para clases virtuales puras. Por lo que efectivamente tiene que hacer un trabajo que el compilador de Java lo hace de forma automática:

// El equivalente de C++ clase

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

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

Otra buena regla a seguir en C++ es, cada vez que se hereda de una clase base con pura métodos virtuales, volver a declararlos en la clase derivada, incluso si los deja como virtuales puros. No afecta el rendimiento y permite que todos sepan que el objeto es solo una implementación parcial.