Ярлыки

понедельник, 28 марта 2011 г.

Qt Ошибка «undefined reference to vtable»

Программируя на Qt и время от времени проверяя форумы, приходится наблюдать, что программисты часто борятся с сообщениями вида «undefined reference to vtable».
Попробую описать ситуации, когда может возникнуть данная ошибка, и дать несколько советов, как избежать её.
Чаще всего данное сообщение всплывает, если вы используете MOC.
MOC (Meta-Object Compiler, переводится как Метаобъектная система/компилятор) – механизм, который расширяет синтаксис C++.
MOC:
  • необходим для механизма сигналов-слотов;
  • позволяет программисту получать метаинформацию о подклассах QObject (QObject::metaObject());
  • используется для интернационализации приложений (QObject::tr());
  • содержит в себе полезные расширения синтаксиса C++.
MOC компилятор находит и обрабатывает все заголовочные проекта.
При появлении ошибки «undefined reference to vtable», первым делом очистите проект и пересоберите его, не забыв запустить qmake:
make clean
# удалите вручную все moc-файлы, если они остались после выполнения make clean
rm Makefile
qmake
make

Если ошибка не исчезла, то проверьте файл проекта (*.pro). Проверьте, чтобы все необходимые заголовочные файлы (включающие сигналы и слоты особенно) были включены в проект. Список заголовочных файлов должен содержаться в переменной HEADERS:
HEADERS += firstHeaderFile.h \
otherHeaderFile.h \
secondHeaderFile.h

Если вы используете в проекте папку, содержащую заголовочные файлы, убедитесь, что добавили эту папку в переменную INCLUDEPATH:
INCLUDEPATH += ./include
Если обнаружили забытый h-, hpp-файл – пересоберите проект заново.
Проблема осталась? Тогда ещё варианты:
  1. Загляните в c-, cpp-файлы и убедитесь, что классы там не определяются. MOC разбирает только заголовочные файлы;
  2. Каждый класс, который имеет сигналы или слоты должен наследоваться от QObject, напрямую или нет.
    В начале определения таких классов обязательно должнен стоять макрос Q_OBJECT, к примеру:class MyWidget : public QWidget
    {
    Q_OBJECT
    /* класс наследуется от QWidget, а значит и от QObject,
    * следовательно должен содержать макрос Q_OBJECT.
    * Если класс наследуется от QObject, но не содержит сигналов или слотов,
    * то не обязательно включать макрос в описание класса.
    */
    ...
    }
  3. Ещё одна вещь, которую можно проверить.
    Удалите Makefile и посмотрите, что выдаст qmake в Makefile:

    rm Makefile
    qmake

    Откройте Makefile редактором и найдите следующие строки:

    compiler_moc_header_make_all:
    и
    compiler_moc_header_clean:

    Например, в моем Makefile есть:mocclean: compiler_moc_header_clean compiler_moc_source_clean
    mocables: compiler_moc_header_make_all compiler_moc_source_make_all

    compiler_moc_header_make_all:
    release/moc_mainwindow.cpp release/moc_SystemConfiguration.cpp release/moc_InstrumentConfiguration.cpp release/moc_ModuleConfiguration.cpp release/moc_ChannelConfiguration.cpp
    compiler_moc_header_clean:
    -$(DEL_FILE) release\moc_mainwindow.cpp release\moc_SystemConfiguration.cpp release\moc_InstrumentConfiguration.cpp release\moc_ModuleConfiguration.cpp release\moc_ChannelConfiguration.cpp

    Каждый заголовочный файл, содержащийся в проекте и
    нуждающийся в обработке MOC-компилятора, должен находиться в этих строках.
    Имя файла будет начинаться с прифекса «moc_».
    Проверьте, что в список включены все h-файлы (точнее, только те, которые требуют обработку MOC-компилятором).
    Если Вы не обнаружили какой-либо moc-файл, не спешите править руками Makefile,
    просто подправьте pro-файл нужным образом.
    Если файл проекта правилен, то qmake генерирует правильный Makefile.
link

6 комментариев:

  1. Хорошая статья

    ОтветитьУдалить
  2. Огромное спасибо! Мне даже в голову бы не пришло
    >Загляните в c-, cpp-файлы и убедитесь, что классы там не определяются. MOC разбирает только заголовочные файлы;
    Потому что сейчас писал простенькое приложение, только разбираюсь в языке, поэтому простейший класс и объявлял в основном файле. А тут вон оно как)
    Спасибо!

    ОтветитьУдалить
  3. Спасибо большое!
    >Загляните в c-, cpp-файлы и убедитесь, что классы там не определяются. MOC разбирает только заголовочные файлы;

    Тоже ломала голову, почему все заработало, когда перенесла объявление класса в hник.

    ОтветитьУдалить
  4. Если же все-таки класс определен в CPP, например, в main.cpp, и нет желания делать для него отдельные h/cpp файлы, можно включить его moc-файл явно, вот так:

    ---
    [main.cpp]

    #include
    #include

    class MyObject : public QObject
    {
    Q_OBJECT
    };

    int main( int argc, char** argv )
    {
    QApplication app( argc, argv );
    MyObject ob;
    return app.exec();
    }

    #include "main.moc"

    ОтветитьУдалить
  5. Большое спасибо! Не знал насчет заголовочных файлов!

    ОтветитьУдалить
  6. Этот комментарий был удален автором.

    ОтветитьУдалить