Ошибка компоновщика Qt: "неопределенная ссылка на vtable"

Это мой заголовок:

#ifndef BARELYSOCKET_H
#define BARELYSOCKET_H

#include <QObject>
//! The First Draw of the BarelySocket!

class BarelySocket: public QObject
{
    Q_OBJECT

public:
    BarelySocket();
public slots:
    void sendMessage(Message aMessage);
signals:
    void reciveMessage(Message aMessage);

private:
    //   QVector<Message> reciveMessages;
};

#endif // BARELYSOCKET_H

Это мой класс:

#include <QTGui>
#include <QObject>
#include "type.h"
#include "client.h"
#include "server.h"

#include "barelysocket.h"

BarelySocket::BarelySocket()
{
    //this->reciveMessages.clear();
    qDebug("BarelySocket::BarelySocket()");
}

void BarelySocket::sendMessage(Message aMessage)
{
}

void BarelySocket::reciveMessage(Message aMessage)
{
}

Я получаю ошибку компоновщика:

undefined reference to 'vtable for BarelySocket'
  • Это подразумевает, что у меня виртуальный метод не реализован. Но в моем классе нет виртуальных методов.
  • Я прокомментировал вектор, думая, что это было причиной, но ошибка не исчезла.
  • Message это комплекс struct, но даже используя int вместо этого не исправить вещи.

9 ответов

Каждый раз, когда вы добавляете новый вызов в макрос Q_OBJECT, вам нужно снова запускать qmake. Проблема vtables, на которую вы ссылаетесь, напрямую связана с этим.

Просто запустите qmake, и у вас все получится, если в вашем коде нет других проблем.

Я видел много способов решить проблему, но не объяснил, почему это происходит, так что здесь.

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

В общем, проблема может быть решена путем генерации vtable в каждом TU, где определен класс, и затем позволяет компоновщику устранять дубликаты. Поскольку определения классов должны быть одинаковыми в каждом случае ODR, это безопасно. Однако это также замедляет компиляцию, раздувает объектные файлы и требует от компоновщика дополнительной работы.

Поэтому в качестве оптимизации компиляторы, по возможности, выбирают конкретный TU для помещения vtable. В общем C++ ABI этот TU является тем, в котором реализована ключевая функция класса, где ключевая функция является первая виртуальная функция-член, которая объявлена ​​в классе, но не определена.

В случае классов Qt они обычно начинаются с макроса Q_OBJECT, и этот макрос содержит объявление

virtual const QMetaObject *metaObject() const;

которая, поскольку она является первой виртуальной функцией в макросе, обычно будет первой виртуальной функцией класса и, следовательно, его ключевой функцией. Поэтому компилятор не будет генерировать vtable в большинстве TU, а только тот, который реализует metaObject, И реализация этой функции записывается автоматически moc когда он обрабатывает заголовок. Таким образом, вам нужно иметь moc обработайте ваш заголовок, чтобы сгенерировать новый файл.cpp, а затем включите файл.cpp в свою компиляцию.

Поэтому, когда у вас есть новый заголовок, который определяет QObjectкласс, вам нужно перезапустить qmake чтобы он обновлял ваши make-файлы для запуска moc на новый заголовок и скомпилируйте получившийся файл.cpp.

Я столкнулся с этой ошибкой после того, как создал небольшой класс внутри небольшого файла "main.cpp", который я создал для проверки чего-либо.

Спустя час или около того, я наконец переместил этот класс из main.cpp в отдельный hpp-файл, обновил файл.pro (project), и проект был прекрасно скомпонован. Это, возможно, не было проблемой здесь, но я полагал, что это было бы полезно в любом случае.

Из опыта: часто qmake && make clean && make помогает. Я лично чувствую, что иногда обнаружение изменений / эффекты кэширования / что-то, чего я не знаю, ххххх. Я не могу сказать почему, но это первое, что я делаю, когда сталкиваюсь с такой ошибкой.

Кстати. есть опечатка в> recive <

Вы забыли вызвать конструктор QObject в своем конструкторе (в списке инициализатора). (Это не устраняет ошибку, хотя)

Для меня я заметил из журналов сборки, что moc не вызывался. Очистить все не помогло. Поэтому я удалил.pro.user, перезапустил IDE, и он добился цели.

Сигналы не должны иметь реализацию (это будет сгенерировано Qt). Удалить reciveMessage реализация из вашего.cpp файла. Это может решить вашу проблему.

Еще одна вещь, которую я видел: с BarelySocket Класс, наследуемый от QObject, должен иметь виртуальный деструктор, чтобы избежать проблем при уничтожении. Это должно быть сделано для всех классов, которые наследуются от другого класса.

Когда вы наследуете класс из QOBject (и используете макрос Q_OBJECT), не забудьте специально определить и создать классы конструктора и деструктора. Недостаточно использовать конструктор / деструкторы компилятора по умолчанию. Рекомендации по очистке / запуску qmake (и очистке ваших файлов moc_) по-прежнему актуальны. Это исправило мою похожую проблему.

Я боролся с этой ошибкой часов. Решил это, поместив.cpp и.h файл в отдельную папку (!!) . Затем добавили папку в.pro-файл: INCLUDEPATH += $${_PRO_FILE_PWD_}/../MyClasses/CMyClassWidget

а затем добавил.cpp и.h файл. Работает наконец.

Я нашел еще одну причину, почему вы могли бы видеть это - так как qmake анализирует ваши файлы классов, если вы изменили их нестандартным способом, вы можете получить эту ошибку. В моем случае у меня был собственный диалог, унаследованный от QDialog, но я только хотел, чтобы он компилировался и запускался при сборке для Linux, а не для Windows или OSX. я просто #ifdef __linux__ класс, чтобы он не компилировался, но в Linux, хотя __linux__ было определено, что скинул qmake,

Другие вопросы по тегам