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 代码需要一些改进:
- 永远不要在 CMake 中使用
include_directories
。请改用 target_include_directories
。
- 始终将
PRIVATE
、PUBLIC
或 INTERFACE
之一与 target_link_libraries
结合使用,以避免奇怪的遗留行为。
- 用
${PROJECT_NAME}
术语定义目标很奇怪,而且会更长,并且会降低代码的可读性,没有任何好处。没有令人信服的理由让它们保持耦合,项目名称很少更改。
- 切勿在 CMakeLists.txt
中设置 CMAKE_CXX_COMPILER
- 编译功能优先于设置
CMAKE_CXX_STANDARD
。
- 我严重怀疑您使用的是 CMake 3.1.0。永远不要将版本设置为低于您正在使用/测试的版本。
- 您在
CMAKE_AUTOUIC_ON
中有一个杂散的下划线
改为使用此版本:
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=].
我决定用 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 代码需要一些改进:
- 永远不要在 CMake 中使用
include_directories
。请改用target_include_directories
。 - 始终将
PRIVATE
、PUBLIC
或INTERFACE
之一与target_link_libraries
结合使用,以避免奇怪的遗留行为。 - 用
${PROJECT_NAME}
术语定义目标很奇怪,而且会更长,并且会降低代码的可读性,没有任何好处。没有令人信服的理由让它们保持耦合,项目名称很少更改。 - 切勿在 CMakeLists.txt 中设置
- 编译功能优先于设置
CMAKE_CXX_STANDARD
。 - 我严重怀疑您使用的是 CMake 3.1.0。永远不要将版本设置为低于您正在使用/测试的版本。
- 您在
CMAKE_AUTOUIC_ON
中有一个杂散的下划线
CMAKE_CXX_COMPILER
改为使用此版本:
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=].