简体   繁体   English

使用 Qt 对 vtable 的未定义引用

[英]Undefined reference to vtable using Qt

I'm new to Qt and I'm trying to do a simple VoIP application.我是 Qt 的新手,我正在尝试做一个简单的 VoIP 应用程序。 I use CMake and conan to get all packages and build the application.我使用 CMake 和 conan 来获取所有包并构建应用程序。 If I place all Qt related class headers and sources files on the same directory, I can compile without any problem, but when I move the header files to another directory I found linker problems.如果我将所有 Qt 相关的类头文件和源文件放在同一目录中,我可以毫无问题地编译,但是当我将头文件移动到另一个目录时,我发现链接器问题。 I don't know why because in CMakeLists.txt I declare the include directories (and is working fine for others classes not related to Qt), I feel it's something related to autoMOC.我不知道为什么,因为在 CMakeLists.txt 中我声明了包含目录(并且对于与 Qt 无关的其他类工作正常),我觉得这与 autoMOC 相关。

Project structure项目结构

|--include
|     |
|     |--client
|     |    |
|     |    |--views
|     |    |    |
|     |    |    |--loginwindow.ui
|     |    |    |--ui_loginwindow.h
|     |    |    |--mainwindow.ui
|     |    |    |--ui_mainwindow.h
|     |    |--Client.h
|     |    |--loginWindow.h
|     |    |--mainWindow.h
|     |    |--Profile.h
|     |--common
|     |
|     |--server
|     |
|--src
|   |
|   |--client
|   |   |
|   |   |--Controller
|   |   |--Model
|   |   |--View
|   |   |    |
|   |   |    |--loginWindow.cpp
|   |   |    |--mainWindow.cpp
|   |--common
|   |
|   |--server
|   |

I've ommited some directories content because the problem is not found there.我省略了一些目录内容,因为那里没有发现问题。

CMakeLists.txt CMakeLists.txt

if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
    message(FATAL_ERROR "Prevented in-tree built. Please create a build directory outside of the source code and call cmake from there")
endif ()

list(APPEND CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR})
list(APPEND CMAKE_PREFIX_PATH ${CMAKE_BINARY_DIR})

project(babel)
cmake_minimum_required(VERSION 3.17.4)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_definitions("-fPIC")

if(MSVC)
    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
else()
    set(STANDARD_UNIX_CXX_FLAGS "-Wall -g3 -Wextra -Wfatal-errors")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${STANDARD_UNIX_CXX_FLAGS}")
endif()

if (EXISTS ${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
    include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
else()
    message(FATAL_ERROR "No conanbuildinfo.cmake file found")
endif()

conan_basic_setup(KEEP_RPATHS)

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

find_package(Qt5OpenGL CONFIG REQUIRED)
find_package(Qt5Widgets CONFIG REQUIRED)
find_package(Qt5Core CONFIG REQUIRED)
find_package(Qt5Gui CONFIG REQUIRED)
find_package(Qt5Network CONFIG REQUIRED)
find_package(portaudio REQUIRED)
find_package(Opus REQUIRED)
find_package(asio REQUIRED)

file(
        GLOB_RECURSE
        SOURCES_CLIENT
        ${PROJECT_SOURCE_DIR}/src/*.cpp
        ${PROJECT_SOURCE_DIR}/src/client/*.cpp
        ${PROJECT_SOURCE_DIR}/include/client/*.hpp
        ${PROJECT_SOURCE_DIR}/include/client/*.ui
        ${PROJECT_SOURCE_DIR}/resources.qrc
)
file(
        GLOB_RECURSE
        SOURCES_SERVER
        ${PROJECT_SOURCE_DIR}/src/server/*.cpp
)
file(
        GLOB_RECURSE
        SOURCES_COMMON
        ${PROJECT_SOURCE_DIR}/src/common/*.cpp
        ${PROJECT_SOURCE_DIR}/include/common/*.hpp
)
add_executable(babel_client ${SOURCES_CLIENT} ${SOURCES_COMMON})
install(TARGETS babel_client DESTINATION ${PROJECT_SOURCE_DIR}/bin)
target_link_libraries(
        babel_client
        Qt5::Widgets
        Qt5::Network
        Qt5::OpenGL
        Qt5::Core
        Qt5::Gui
        opus
        portaudio
)
target_include_directories(
        babel_client PRIVATE
        ${CONAN_INCLUDE_LIBS}
        ${PROJECT_SOURCE_DIR}/include/client
        ${PROJECT_SOURCE_DIR}/include/client/views
        ${PROJECT_SOURCE_DIR}/include/common
)

loginWindow.h登录窗口.h


#ifndef LOGINWINDOW_H
#define LOGINWINDOW_H

#include <QMainWindow>
#include "mainWindow.h"
#include "views/ui_loginwindow.h"
#include <QKeyEvent>

QT_BEGIN_NAMESPACE
namespace Ui { class LoginWindow; }
QT_END_NAMESPACE

class LoginWindow : public QMainWindow
{
    Q_OBJECT

public:
    LoginWindow(QWidget *parent = nullptr);
    ~LoginWindow();

private slots:
    void on_loginButton_clicked();

protected:
    void keyPressEvent(QKeyEvent *key);

private:
    Ui::LoginWindow *ui;
    MainWindow *mainWindow;
};
#endif // LOGINWINDOW_H

loginWindow.cpp登录窗口.cpp


#include "loginWindow.h"
#include <QPixmap>

LoginWindow::LoginWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::LoginWindow)
{
    int picWidth;
    int picHeight;

    ui->setupUi(this);
    QPixmap pix(":/resources/babel.png");
    picWidth = ui->babelPicture->width();
    picHeight = ui->babelPicture->height();
    ui->babelPicture->setPixmap(pix.scaled(picWidth, picHeight, Qt::KeepAspectRatio));
    ui->statusbar->addPermanentWidget(ui->statusText);
    ui->loginButton->setDefault(true);
}

LoginWindow::~LoginWindow()
{
    delete (ui);
}

void LoginWindow::keyPressEvent(QKeyEvent *key)
{
    if (key->key() == Qt::Key_Return)
        on_loginButton_clicked();
}

void LoginWindow::on_loginButton_clicked()
{
    QString user = ui->userField->text();
    QString pass = ui->passwordField->text();

    if (user == "test" && pass == "test") {
        ui->statusText->setText("Correct login");
        this->hide();
        mainWindow = new MainWindow(this);
        mainWindow->show();
    } else{
        ui->statusText->setText("Inorrect login");
    }

}

mainWindow.h主窗口.h


#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "views/ui_mainwindow.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

mainWindow.cpp主窗口文件


#include "mainWindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

Linker output链接器输出

/usr/bin/ld: CMakeFiles/babel_client.dir/src/client/View/loginWindow.cpp.o: in function `LoginWindow::~LoginWindow()':
/home/bltksk/epitech/tek3/CPP/babel/src/client/View/loginWindow.cpp:20: undefined reference to `vtable for LoginWindow'
/usr/bin/ld: CMakeFiles/babel_client.dir/src/client/View/loginWindow.cpp.o: in function `non-virtual thunk to LoginWindow::~LoginWindow()':
/home/bltksk/epitech/tek3/CPP/babel/src/client/View/loginWindow.cpp:23: undefined reference to `vtable for LoginWindow'
/usr/bin/ld: CMakeFiles/babel_client.dir/src/client/View/loginWindow.cpp.o: in function `LoginWindow::LoginWindow(QWidget*)':
/home/bltksk/epitech/tek3/CPP/babel/src/client/View/loginWindow.cpp:6: undefined reference to `vtable for LoginWindow'
/usr/bin/ld: CMakeFiles/babel_client.dir/src/client/View/mainWindow.cpp.o: in function `MainWindow::MainWindow(QWidget*)':
/home/bltksk/epitech/tek3/CPP/babel/src/client/View/mainWindow.cpp:5: undefined reference to `vtable for MainWindow'
/usr/bin/ld: CMakeFiles/babel_client.dir/src/client/View/mainWindow.cpp.o: in function `MainWindow::~MainWindow()':
/home/bltksk/epitech/tek3/CPP/babel/src/client/View/mainWindow.cpp:10: undefined reference to `vtable for MainWindow'
/usr/bin/ld: CMakeFiles/babel_client.dir/src/client/View/mainWindow.cpp.o: in function `non-virtual thunk to MainWindow::~MainWindow()':
/home/bltksk/epitech/tek3/CPP/babel/src/client/View/mainWindow.cpp:13: undefined reference to `vtable for MainWindow'

As I've said on the introduction, if I place xxWindow.h and xxWindow.cpp on the same folder, the problem disappears, but when I place them on the include directory it doesn't link.正如我在介绍中所说,如果我将 xxWindow.h 和 xxWindow.cpp 放在同一个文件夹中,问题就会消失,但是当我将它们放在包含目录中时,它不会链接。 I'm not a cmake expert, but it seems right to me.我不是 cmake 专家,但对我来说似乎是正确的。 Any hint about the solution?有关解决方案的任何提示?

Okay, I achieved to solve the problem.好的,我实现了解决问题。

In order to make AUTOUIC function correctly, we have to add all the headers that includes ui_*.h to the sources target and not only to the include path.为了使 AUTOUIC 正确运行,我们必须将包含 ui_*.h 的所有头文件添加到目标,而不仅仅是包含路径。 This way automoc will correctly process the headers and it will be correctly linked.这样 automoc 将正确处理标题并正确链接。

In my case it is like this:就我而言,它是这样的:

file(
        GLOB_RECURSE
        SOURCES_CLIENT
        ${PROJECT_SOURCE_DIR}/src/*.cpp
        ${PROJECT_SOURCE_DIR}/src/client/*.cpp
        ${PROJECT_SOURCE_DIR}/include/client/*.h
        ${PROJECT_SOURCE_DIR}/include/client/resources/resources.qrc
)

Also, remember to set CMAKE_AUTOUIC_SEARCH_PATHS (cmake version >3.9) with the path where .ui files are located, if not, there will be an AutoUic error.另外记得把CMAKE_AUTOUIC_SEARCH_PATHS(cmake version >3.9)设置成.ui文件所在的路径,否则会出现AutoUic错误。

I hope this will help somebody in the future.我希望这会在将来对某人有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM