Qt 和 CMake 因重复符号而失败

Posted

技术标签:

【中文标题】Qt 和 CMake 因重复符号而失败【英文标题】:Qt and CMake fails with duplicate symbols 【发布时间】:2017-10-29 09:51:49 【问题描述】:

我的 c++/qt 项目中有 3 个文件,我正在使用 CMake。我正在尝试编译它这里有一些代码:

CMakeLists 包含:

cmake_minimum_required(VERSION 3.8)
project(untitled)

set(CMAKE_CXX_STANDARD 14)

set(CMAKE_PREFIX_PATH /Users/username/Qt/5.9.2/clang_64/)

set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)

find_package(Qt5Core)
find_package(Qt5Network)

set(SOURCE_FILES main.cpp server.cpp)

add_executable(untitled $SOURCE_FILES)

target_link_libraries($PROJECT_NAME Qt5::Core)
target_link_libraries($PROJECT_NAME Qt5::Network)

Main.cpp 包含:

#include <iostream>
#include <QCoreApplication>
#include <QtDebug>
#include "server.cpp"

int main(int argc, char *argv[])

    QCoreApplication app(argc, argv);
    MyTcpServer server;
    return app.exec();

最后 server.cpp 包含:

#include <QObject>
#include <QTcpSocket>
#include <QTcpServer>
#include "server.moc"

class MyTcpServer : public QObject

Q_OBJECT
public:
    explicit MyTcpServer(QObject *parent = 0);

public slots:
    void slotNewConnection();
    void slotServerRead();
    void slotClientDisconnected();

private:
    QTcpServer * mTcpServer;
    QTcpSocket * mTcpSocket;
;

MyTcpServer::MyTcpServer(QObject *parent) : QObject(parent)

  ...


void MyTcpServer::slotNewConnection()

  ...


void MyTcpServer::slotServerRead()

  ...


void MyTcpServer::slotClientDisconnected()

    mTcpSocket->close();

我正在尝试使用 CMake 编译我的项目,当我运行 CMake 时,我遇到了这个问题:

duplicate symbol __ZN11MyTcpServer18qt_static_metacallEP7QObjectN11QMetaObject4CallEiPPv in:
    CMakeFiles/untitled.dir/main.cpp.o
    CMakeFiles/untitled.dir/server.cpp.o
                      ...
duplicate symbol __ZN11MyTcpServer16staticMetaObjectE in:
    CMakeFiles/untitled.dir/main.cpp.o
    CMakeFiles/untitled.dir/server.cpp.o
ld: 13 duplicate symbols for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

告诉我有一个重复的符号。 如何解决?

【问题讨论】:

包含.cpp 文件被认为是一种非常糟糕的做法。只需将 MyTcpServer 类的声明移动到单独的头文件 server.h 中,将其包含到 main.cpp 中并从 server.cpp 中删除 server.moc 的包含。 除了 Dmitry:由于 main.cpp 中的 #include "server.cpp"server.cpp 中的所有内容都被编译了两次,第一次是 main.cpp 的一部分,第二次是 server.cpp,因为你列出了两者文件为SOURCE_FILES。从SOURCE_FILES 中删除后者也可以解决您的问题,但我强烈推荐 Dmitry 所做的。 与您的问题无关,但我建议在您的add_executable 中也使用$PROJECT_NAME。这使得 CMakeLists.txt 更容易重复使用。 【参考方案1】:

最好不要将#include 用于.cpp 文件。将定义和声明拆分为不同的文件是一种很好的做法。

(其中一个例外是 PIMPL 模式的私有声明。)

如果您想避免拆分,因为您只有一小段代码,请使用头文件并在类的定义中实现您的方法。

如果实现了一个库:不要安装头文件,以防你的类不能从外部访问。

【讨论】:

【参考方案2】:

如 cmets 中所述,您的情况的简单解决方法是来自您的 cpp 的 fo#include "server.cpp"(很少需要这样做,而且几乎从来都不是一件好事)。

第二件事是将#include "server.moc" 行移动到cpp 文件的末尾。这一点很重要,因为该文件包含一些使用Q_OBJECT 注入的成员函数的实现,并且需要在实现其主体之外的成员函数之前定义该类。

【讨论】:

以上是关于Qt 和 CMake 因重复符号而失败的主要内容,如果未能解决你的问题,请参考以下文章

为什么更新因唯一索引而失败而没有重复值?

jQuery选择器因字符串中的双引号而失败[重复]

我有一个字符串,我需要根据自定义顺序进行排序,并且我正在使用优先级队列,但优先级队列因重复而失败 [重复]

当类实现相同的接口时,Lambda表达式因LambdaConversionException而失败? [重复]

通用代码因 Spring 数据和 Querydsl 而失败

Mac上的Qt说找不到架构x86_64的符号[重复]