Qt5 未定义引用 vtable CMake 构建问题

Qt5 undefined reference to vtable CMake build issue

我决定用 CMake 构建一些计算器,但由于某种原因失败了,但它看起来是正确的。我有一个 set(CMAKE_AUTOMOC ON) 规则,但是什么是 vtable?我不能简单地构建它...下面有所有项目文件

CMakeLists.txt

cmake_minimum_required(VERSION 3.1.0)

project(calculator)

set(CMAKE_CXX_COMPILER g++)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC_ON)
set(CMAKE_AUTORCC ON)

find_package(Qt5 COMPONENTS Core Widgets REQUIRED)

include_directories("./include")

add_executable(${PROJECT_NAME} calculator.cpp main.cpp)

target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Widgets)

calculator.hpp

#ifndef CALCULATOR_HPP
# define CALCULATOR_HPP

#include <QtWidgets/QWidget>
#include <QVector>
#include <QLCDNumber>
#include <QPushButton>

class Calculator : public QWidget {
    Q_OBJECT
public:
    Calculator(QWidget *pwgt = nullptr);
    QPushButton         *createButton(QString const &rhs);
    void                calculate();
private:
    QLCDNumber          *_plcd;
    QString             _strDisplay;
    QVector<QString>    _strVector;
public slots:
    void                buttonClicked();
};

#endif

calculator.cpp

#include <calculator.hpp>
#include <QGridLayout>

Calculator::Calculator(QWidget *pwgt) : QWidget(pwgt) {
    QGridLayout *gridObj = new QGridLayout();

    _plcd = new QLCDNumber(12);
    _plcd->setMode(QLCDNumber::Dec);
    _plcd->setSegmentStyle(QLCDNumber::Flat);
    _plcd->setMinimumSize(150, 50);

    //_wgtPtr = (!_pwgt) ? new QWidget() : pwgt;
    QChar   calcChars[4][4] = { {'7', '8', '9', '/'},
                                {'4', '5', '6', '*'},
                                {'1', '2', '3', '-'},
                                {'0', '.', '=', '+'}};
    gridObj->addWidget(_plcd, 0, 0);
    gridObj->addWidget(createButton("CE"), 1, 3);
    for (int i = 0; i < 4; ++i) {
        for (int j = 0;  j < 4; ++j)
            gridObj->addWidget(createButton(calcChars[i][j]), i + 2, j);
    }
    setLayout(gridObj);
}

QPushButton *Calculator::createButton(QString const &rhs) {
    QPushButton *calcButton = new QPushButton(rhs);
    calcButton->setMinimumSize(40, 40);
    connect(calcButton, SIGNAL(clicked()), SLOT(buttonClicked()));
    return (new QPushButton(rhs));
}

void        Calculator::buttonClicked() {
    QString match = dynamic_cast<QPushButton *>(sender())->text();
    QRegExp regMatch("[0-9]");

    if (!match.compare("CE")) {
        _strVector.clear();
        _strDisplay.clear();
        _plcd->display("0");
    } else if (match.contains(regMatch)) {
        _strDisplay.push_back(match);
        _plcd->display(_strDisplay.toDouble());
    } else if (!match.compare(".")) {
        _strDisplay.push_back(match);
        _plcd->display(_strDisplay.toDouble());
    } else {
        _strVector.push_back(_strDisplay);
        if (_strVector.size() > 1) {
            calculate();
            _strVector.clear();
            _strVector.push_back(_strDisplay);
            if (match.compare("="))
                _strVector.push_back(match);
        } else {
            _strVector.push_back(_strDisplay);
            _strDisplay.clear();
            _plcd->display("0");
        }
    }
}

void        Calculator::calculate() {
    double  rValue = _strVector.back().toDouble();
    _strVector.pop_back();
    QString operCalc = _strVector.back();
    _strVector.pop_back();
    double  lValue = _strVector.back().toDouble();

    if (!operCalc.compare("+"))
        rValue += lValue;
    if (!operCalc.compare("-"))
        rValue -= lValue;
    if (!operCalc.compare("/"))
        rValue /= lValue;
    if (!operCalc.compare("*"))
        rValue *= lValue;
    _strDisplay.number(rValue);
    _plcd->display(_strDisplay.toDouble());
}

main.cpp

#include <calculator.hpp>
#include <QApplication>

int main(int argc, char *argv[]) {
    QApplication    app(argc, argv);
    Calculator  *obj = new Calculator;
    obj->resize(230, 200);
    obj->setWindowTitle("QtCalculator");
    obj->show();

    return (app.exec());
}

这是文件层次结构

|--> CMakeLists.txt
|
|--> calculator.cpp
|
|--> main.cpp
|
|--> include
     |
     |--> calculator.hpp

这是构建输出(在 CLion IDE 中)

====================[ Build | all | Debug ]=====================================
/snap/clion/162/bin/cmake/linux/bin/cmake --build /home/lchantel/train_01Qt/cmake-build-debug --target all -- -j 3
[ 20%] Automatic MOC for target calculator
[ 20%] Built target calculator_autogen
Scanning dependencies of target calculator
[ 40%] Building CXX object CMakeFiles/calculator.dir/calculator_autogen/mocs_compilation.cpp.o
[ 60%] Building CXX object CMakeFiles/calculator.dir/calculator.cpp.o
[ 80%] Building CXX object CMakeFiles/calculator.dir/main.cpp.o
[100%] Linking CXX executable calculator
/usr/bin/ld: CMakeFiles/calculator.dir/calculator.cpp.o: in function `Calculator::Calculator(QWidget*)':
/home/lchantel/train_01Qt/calculator.cpp:4: undefined reference to `vtable for Calculator'
/usr/bin/ld: /home/lchantel/train_01Qt/calculator.cpp:4: undefined reference to `vtable for Calculator'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/calculator.dir/build.make:126: calculator] Error 1
make[1]: *** [CMakeFiles/Makefile2:84: CMakeFiles/calculator.dir/all] Error 2
make: *** [Makefile:91: all] Error 2

您的 CMake 代码需要一些改进:

  1. 永远不要在 CMake 中使用 include_directories。请改用 target_include_directories
  2. 始终将 PRIVATEPUBLICINTERFACE 之一与 target_link_libraries 结合使用,以避免奇怪的遗留行为。
  3. ${PROJECT_NAME} 术语定义目标很奇怪,而且会更长,并且会降低代码的可读性,没有任何好处。没有令人信服的理由让它们保持耦合,项目名称很少更改。
  4. 切勿在 CMakeLists.txt
  5. 中设置 CMAKE_CXX_COMPILER
  6. 编译功能优先于设置 CMAKE_CXX_STANDARD
  7. 严重怀疑您使用的是 CMake 3.1.0。永远不要将版本设置为低于您正在使用/测试的版本。
  8. 您在 CMAKE_AUTOUIC_ON
  9. 中有一个杂散的下划线

改为使用此版本:

cmake_minimum_required(VERSION 3.16)
project(calculator)

set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)

find_package(Qt5 REQUIRED Core Widgets)

add_executable(calculator calculator.cpp main.cpp include/calculator.hpp)
target_link_libraries(calculator PRIVATE Qt5::Core Qt5::Widgets)
target_include_directories(calculator PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include")
target_compile_features(calculator PRIVATE cxx_std_11)

您看到 vtable 错误的原因是 moc 没有看到您的 include/calculator.hpp header 因为它没有被列为 [= 中的来源之一23=].