Подтвердить что ты не робот

Помещение объявления класса в файл .cpp

Возможно ли иметь объявление класса и реализацию в том же файле .cpp?

Я хочу выполнить некоторые модульные тесты с помощью mock-объекта. Вот пример моего теста:

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

Как вы можете видеть, класс ConnectionMockup используется только классом TestConnection, и мне он больше не нужен. Поэтому, когда я пытаюсь скомпилировать эту программу, я получаю следующую ошибку:

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

Можно ли оставить объявление здесь, или я должен создать файл заголовка и перенести объявление в этот файл?

EDIT:. Поскольку г-н Джерри Коффин (спасибо, г-н Коффин) предложил, чтобы у меня не было каких-либо виртуальных функций, я приведу здесь объявление AbstractConnection, чтобы мы могли рассмотреть эту возможность:

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

};

РЕШЕНИЕ: Благодаря @JCooper, @iammilind и @Jerry Coffin у нас есть решение. После удаления деструктора из AbstractConnection (поскольку он фактически ничего не делает) и удаления Q_OBJECT из ConnectionMockup он работает.

4b9b3361

Ответ 1

Да, это полностью законно и допустимо определить класс и его функции-члены в одном файле. На самом деле, с точки зрения компилятора, который по существу всегда имеет место, вы имеете определение класса в заголовке и включаете этот заголовок в исходный файл, где вы реализуете его функции-члены.

Ошибки, с которыми вы столкнулись, выглядят как ошибки компоновщика, а не ошибки компилятора. То, что пропало, не совсем ясно из того, что вы опубликовали. Одна из возможностей заключается в том, что ваш базовый класс имеет некоторые чистые виртуальные машины, которые вы не смогли реализовать в производном классе, но я не совсем уверен, что это правильно.

Ответ 2

Макрос Q_OBJECT объявляет набор функций-членов метаобъекта. Инструмент сборки MOC отвечает за разбор .h файлов и определение этих объявлений функций. Обратите внимание, что он не анализирует файлы .cpp. В вашем случае vtable не может быть найден, потому что инструмент MOC не проанализировал ваш .cpp файл. Решение состоит в том, чтобы переместить определение класса внутри файла заголовка и добавить заголовок в ваш .pro файл. Второе решение - бит "взломанный" - это сделать следующее:

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

Обратите внимание на `#include "myfile.moc" в определении класса.

Это работает, потому что qmake будет вызывать инструмент MOC для любых файлов с директивой #include. Таким образом, MOC будет анализировать файл .cpp и генерировать определения функции метаобъекта, устраняя ошибку компоновщика.

Ответ 3

Если у базового класса есть какая-либо функция virtual, которая не является чистой, это определение должно быть включено при компиляции окончательного двоичного кода, в противном случае оно дает ошибку компоновщика для vtable или typeinfo. Пример ниже:

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

Теперь компиляция для Derived.cpp и Base.cpp будет работать нормально. Оба .cpp файла также могут быть скомпилированы отдельно для создания объектных файлов, а затем связаны между собой.

Из вашего вопроса, я считаю, что вы не каким-то образом прикрепляете файл .cpp/object от class AbstractConnection, который по-прежнему содержит одну нечистую виртуальную функцию - ее destructor. Если вы скомпилируете это определение вместе с вашим ConnectionMockup, тогда ошибка компоновщика не должна появляться. Либо вы можете скомпилировать файл, включая тело деструктора, либо определить тело деструктора в самом определении класса.