2011-04-14 6 views
17

¿Es posible tener declaración de clase e implementación en el mismo archivo .cpp?Poner declaración de clase en el archivo .cpp

Quiero hacer algunas pruebas unitarias con la ayuda de un objeto simulado. Aquí está un cierto ejemplo de mi prueba:

// Some includes removed 

#include "abstractconnection.h" 

class ConnectionMockup : public AbstractConnection 
{ 
    Q_OBJECT 
public: 
    explicit ConnectionMockup(QObject *parent = 0); 

    bool isReady() const; 
    void sendMessage(const QString &message); 

    void test_send_message(const QString &message); 

    bool ready; 
    QStringList messages; 
}; 

ConnectionMockup::ConnectionMockup(QObject *parent) 
    : AbstractConnection(parent) 
{ 
    ready = true; 
} 

bool ConnectionMockup::isReady() const 
{ 
    return ready; 
} 

void ConnectionMockup::sendMessage(const QString &message) 
{ 
    messages.append(message); 
} 

void ConnectionMockup::test_send_message(const QString &message) 
{ 
    emit messageRecieved(message); 
} 

TestEmcProgram::TestEmcProgram(QObject *parent) : 
    QObject(parent) 
{ 
} 

void TestEmcProgram::open() 
{ 
    ConnectionMockup mockup; 
    EmcProgram program(&mockup); 
    QCOMPARE(... 
... 
... 

Como se puede ver, el ConnectionMockup clase sólo se utiliza por TestConnection clase, y yo no lo necesita en otro sitio. Por lo tanto, cuando intento compilar este programa, consigo el error siguiente:

> testemcprogram.o: In function 
> `ConnectionMockup': 
> /home/sasa/Desktop/QtPro/FocoKernel-build-desktop/../FocoKernel/testemcprogram.cpp:29: 
> undefined reference to `vtable for 
> ConnectionMockup' 
> /home/sasa/Desktop/QtPro/FocoKernel-build-desktop/../FocoKernel/testemcprogram.cpp:29: 
> undefined reference to `vtable for 
> ConnectionMockup' testemcprogram.o: In 
> function `~ConnectionMockup': 
> /home/sasa/Desktop/QtPro/FocoKernel-build-desktop/../FocoKernel/testemcprogram.cpp:14: 
> undefined reference to `vtable for 
> ConnectionMockup' 

¿Es posible salir de declaración aquí, o tengo que crear el archivo de cabecera y mover declaración a ese archivo?

EDIT: Dado que el Sr. Jerry Ataúd (Gracias Sr. Ataúd) sugirió que quizá no tenga algunas funciones virtuales implementadas, voy a poner aquí declaración de AbstractConnection para que pudiéramos revisar esa posibilidad:

#include <QObject> 

class AbstractConnection : public QObject 
{ 
    Q_OBJECT 
public: 
    explicit AbstractConnection(QObject *parent = 0); 
    virtual ~AbstractConnection(); 

    virtual bool isReady() const = 0; 

signals: 
    void messageRecieved(const QString &message); 

public slots: 
    virtual void sendMessage(const QString &message) = 0; 

}; 

SOLUCIÓN: Gracias a @JCooper, @iammilind y @Jerry Coffin tenemos la solución. Después de eliminar el destructor de AbstractConnection (ya que en realidad no hace nada) y eliminar Q_OBJECT de ConnectionMockup, funciona.

+0

Gracias. Lo corregí en el texto ahora. – Sasa

+0

@Sasa: ​​explícitamente no corrigí la primera oración ... la declaración y la implementación son sinónimos aquí. ;) – Xeo

+0

@ 0A0D: En realidad, no, no lo es. Una declaración de clase se ve así: 'clase myclass;' Algo así como: 'clase myclass {/ * ... * /};' es una definición de clase. Luego puede definir los miembros de la clase por separado de la definición de la clase en sí. –

Respuesta

14

Sí, es completamente legítimo y permitido definir una clase y sus funciones miembro en un solo archivo. De hecho, desde el punto de vista del compilador que esencialmente es siempre el caso, tiene la definición de clase en un encabezado e incluye ese encabezado en el archivo de origen donde implementa sus funciones miembro.

Los errores que ha encontrado parecen errores del enlazador, no de compilador. Exactamente lo que falta no está del todo claro de lo que has publicado. Una posibilidad es que su clase base tenga algunos virtuales puros que no haya podido implementar en la clase derivada, pero no estoy en absoluto seguro que es correcto.

+1

Creo que puedo encontrar causa de error ** pero no solución ** (excepto la declaración de movimiento en el archivo de encabezado). Encontré [aquí] (http://stackoverflow.com/q/177468/353563) un error similar. Supongo que es porque hay un macro Q_OBJECT en mi clase ConnectionMockup y como no tengo un archivo de encabezado, el moc del qt omite esta fuente y no genera funciones adicionales/código/lo que sea. : -? – Sasa

+2

@Sasa No debe _necesitar_ utilizar la macro Q_OBJECT para lo que está haciendo, ya que no está utilizando ningún Signals/Slots específicamente en su Maqueta. ¿Has intentado eliminarlo? – JCooper

+0

@JCooper ¡Gracias! Acabo de eliminar Q_OBJECT de ConnectionMockup y destrucción de AbstractConnection (gracias a @iammilind) y funciona ahora. Pero no comprende cómo funciona la señal emitida en test_send_message, cuando no hay Q_OBJECT presente. – Sasa

3

Cuando la clase base tiene ninguna función virtual que es puro, su definición debe ser incluido al compilar el binario final, de lo contrario, da error de vinculador de vtable o typeinfo. Mire el siguiente ejemplo:

// Base.h 
struct Base { 
    virtual void fun() = 0; 
    virtual ~Base(); 
}; 

// Base.cpp 
#include"Base.h" 
Base::~Base() {} 

// Derived.cpp 
#include"Base.h" 
struct Derived : Base { 
    void fun() {} 
}; 

int main() { 
    Derived d; 
} 

Ahora el compile-link para Derived.cpp y Base.cpp funcionará bien. Ambos archivos .cpp también se pueden compilar por separado para crear archivos de objetos y luego enlazarlos entre sí.

De su pregunta, lo que siento es que, de alguna manera no está adjuntando el archivo .cpp/objeto de class AbstractConnection, que todavía contiene una función virtual no pura - es destructor. Si compila esa definición también junto con su ConnectionMockup, entonces el error del enlazador no debería aparecer. O puede compilar el archivo incluido el cuerpo del destructor o definir el cuerpo del destructor en la propia definición de la clase.

+0

+1 por mencionar destructor. Después de eliminarlo (ya que en realidad no hace nada) obtuve menos errores. Gracias. – Sasa

12

La macro Q_OBJECT declara un conjunto de funciones miembro de metaobjeto. La herramienta de compilación MOC es responsable de analizar archivos .h y definir estas declaraciones de funciones. Tenga en cuenta que no analiza los archivos .cpp. En su caso, no se pudo encontrar el vtable porque la herramienta MOC no analizó su archivo .cpp. La solución es mover su definición de clase dentro de un archivo de cabecera y agregar el encabezado a su archivo .pro.Una segunda solución - un poco "hacky" - es hacer lo siguiente:

#include <QObject> 
#include <QtDebug> 

class Counter : public QObject 
{ 
    Q_OBJECT 

public: 
    Counter() { value = 0; } 
    int getValue() const { qDebug() << "getValue()"; return value; } 

public slots: 
    void setValue(int value); 

signals: 
    void valueChanged(int newValue); 

private: 
    int value; 
}; 

#include "main.moc" 

void Counter::setValue(int value) 
{ 
    qDebug() << "setValue()"; 
    if (this->value != value) { 
    this->value = value; 
    emit valueChanged(value); 
    } 
} 

int main() 
{ 
    Counter a, b; 

    QObject::connect(
    &a, &Counter::valueChanged, 
    &b, &Counter::setValue); 

    a.setValue(12); 
    b.setValue(48); 

    return 0; 
} 

Aviso del `# include "myfile.moc" bajo la definición de clase.

Esto funciona porque qmake invocará la herramienta MOC en cualquier archivo con una directiva #include. Por lo tanto, MOC analizará el archivo .cpp y generará las definiciones de funciones del metaobjeto, resolviendo el error del vinculador.

Cuestiones relacionadas