23

Is it possible to have class declaration and implementation in same .cpp file?

I want to do some unit-testing with help of mock object. Here is some example of my test:

// 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(...
...
...

As you can see, the class ConnectionMockup is only used by class TestConnection, and I don't need it anywhere else. So, when I try to compile this program, I get following error:

> 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'

Is it possible to leave declaration here, or I must create header file and move declaration in to that file?

EDIT: Since Mr. Jerry Coffin (thank you Mr. Coffin) suggested that I may not have some virtual functions implemented, I will put here declaration of AbstractConnection so we could review that possibility:

#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;

};

SOLUTION: Thanks to @JCooper, @iammilind and @Jerry Coffin we have the solution. After removing destructor from AbstractConnection (since it actually does nothing) and removing Q_OBJECT from ConnectionMockup it works.

Sasa
  • 1,597
  • 4
  • 16
  • 33
  • Thank you. I corrected it in text now. – Sasa Apr 14 '11 at 01:42
  • @Sasa: I explicitly not corrected the first sentence.. declaration and implementation are synonymous in here. ;) – Xeo Apr 14 '11 at 01:53
  • @0A0D: Actually, no it's not. A class declaration looks like: `class myclass;` Something like: `class myclass { /* ... */ };` is a class definition. You can then define class members separately from the definition of the class itself. – Jerry Coffin Apr 14 '11 at 01:55
  • @Xeo: No correction is needed -- the terminology you have is already correct. – Jerry Coffin Apr 14 '11 at 01:56
  • Well, I just got lost in these terms :). I thought that: Declaration is when you include header file or use forward declaration. Definition is what you put in header file and implementation is what you put in source file. But I don't know anymore what correct is. :) – Sasa Apr 14 '11 at 02:01
  • 1
    I got confused again too, my bad. [This answer](http://stackoverflow.com/questions/1410563/what-is-the-difference-between-a-definition-and-a-declaration/1410632#1410632) by @sbi is really helpful to differentiate between definition and declaration. :) – Xeo Apr 14 '11 at 02:02

3 Answers3

21

The Q_OBJECT macro declares a set of meta-object member functions. The MOC build tool is responsible for parsing .h files and defining these function declarations. Note that it does not parse .cpp files. In your case, the vtable could not be found because the MOC tool did not parse your .cpp file. The solution is to move your class definition inside a header file and add the header to your .pro file. A second solution - a bit "hacky" - is to do the following:

#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;
}

Notice the `#include "myfile.moc" under the class definition.

This works because qmake will invoke the MOC tool on any files with a #include directive. Thus, MOC will parse the .cpp file and generate the meta-object function definitions, resolving your linker error.

16

Yes, it's entirely legitimate and allowable to define a class and its member functions in a single file. In fact, from the viewpoint of the compiler that's essentially always the case -- you have the class definition in a header, and include that header in the source file where you implement its member functions.

The errors you've encountered look like linker errors, not compiler errors. Exactly what's missing isn't entirely clear from what you've posted. One possibility is that your base class has some pure virtuals that you've failed to implement in the derived class, but I'm not at all sure that's correct.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • 2
    I think I may found cause for error **but not solution** (except moving declaration in header file). I found [here](http://stackoverflow.com/q/177468/353563) similar error. I guess that, it is because there is a macro Q_OBJECT in my class ConnectionMockup and since I don't have header file, the qt's moc skips this source and do not generate additionals functions/code/whatever. :-? – Sasa Apr 14 '11 at 02:26
  • 2
    @Sasa You shouldn't _need_ to use the Q_OBJECT macro for what you're doing since you're not using any Signals/Slots specifically in your Mockup. Have you tried removing it? – JCooper Apr 14 '11 at 03:14
  • @JCooper Thank you! I just removed Q_OBJECT from ConnectionMockup and destruction from AbstractConnection (thanks to @iammilind) and it works now. But, it do not understand how emiting signal in test_send_message works now, when no Q_OBJECT is present. – Sasa Apr 14 '11 at 03:24
  • For QObjects, you can either tell your buildsystem to run moc on the .cpp, or put the declaration in a private header (like something_p.h). I find the latter usually less hassle, especially when the public header also contains a QObject. – Frank Osterfeld Apr 14 '11 at 07:15
3

When Base class has any virtual function which is not pure, it's definition needs to be included while compiling the final binary, otherwise it gives linker error for vtable or typeinfo. Look at below example:

// 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;
}

Now compile-link for Derived.cpp and Base.cpp will work fine. Both .cpp files also can be compiled separately for creating object files and then linked together.

From your question, what I feel is that, you are not somehow attaching the .cpp/object file of class AbstractConnection, which still contains one non pure virtual function -- its destructor. If you compile that definition also along with your ConnectionMockup then the linker error should not appear. Either you can compile the file including destructor body or define destructor body in the class definition itself.

iammilind
  • 68,093
  • 33
  • 169
  • 336
  • +1 for mentioning destructor. After removing it (since it actually does nothing) I got less errors. Thank you. – Sasa Apr 14 '11 at 03:26