运行make遇多重定义错误,修复后又现未定义引用问题求助
问题描述
项目结构
ProjectX ├── Module1 │ ├── Module2 │ │ ├── CMakeLists.txt │ │ ├── src │ │ │ └── Module2.cpp // 包含 #include "Common.h" │ │ └── include │ ├── CMakeLists.txt │ ├── src │ │ └── Class1.cpp // 包含 #include "Common.h" │ │ └── Class2.cpp // 包含 #include "Common.h" │ └── include │ └── Class1.h // 包含 #include "Class2.h" ** 注意 ** ├── Module3 │ ├── CMakeLists.txt │ ├── src │ │ └── Class3.cpp // 包含 #include "Common.h" │ └── include ├── src │ └── NewClass.cc // 新增文件 ├── include │ ├── NewClass.h // 新增文件 │ └── Common.h // 新增文件 └── CMakeLists.txt
Common.h 代码
Common.h 定义了供所有模块使用的NewClass实例:
#ifndef GLOBALS_H #define GLOBALS_H #include "NewClass.h" namespace ProjectX { extern NewClass instance1 = NewClass(); extern NewClass instance2 = NewClass(); extern NewClass instance3 = NewClass(); } #endif // GLOBALS_H
CMake配置
ProjectX/CMakeLists.txt
add_library(${PROJECT_NAME} SHARED src/NewClass.cc include/NewClass.h include/Common.h )
ProjectX/Module1/CMakeLists.txt 及 ProjectX/Module2/CMakeLists.txt
set(PROJECTX_DIR ${PROJECT_SOURCE_DIR}/../) target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECTX_DIR}/include/ )
ProjectX/Module3/CMakeLists.txt
set(PROJECTX_DIR ${PROJECT_SOURCE_DIR}/../../) target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECTX_DIR}/include/ )
注意:项目中有5处#include "Common.h"的引用(对应结构中标注的文件)。
初始编译错误
运行make时出现以下链接错误:
/usr/bin/ld: CMakeFiles/Module1.dir/src/Class1.cpp.o:(.bss+0x10): multiple definition of `ProjectX::instance1'; CMakeFiles/Module1.dir/src/Class2.cpp.o:(.bss+0x10): first defined here /usr/bin/ld: CMakeFiles/Module1.dir/src/Class1.cpp.o:(.bss+0x20): multiple definition of `ProjectX::instance2'; CMakeFiles/Module1.dir/src/Class2.cpp.o:(.bss+0x20): first defined here /usr/bin/ld: CMakeFiles/Module1.dir/src/Class1.cpp.o:(.bss+0x0): multiple definition of `ProjectX::instance3'; CMakeFiles/Module1.dir/src/Class2.cpp.o:(.bss+0x0): first defined here /usr/bin/ld: Module2/libModule2.a(Module2.cpp.o):(.bss+0x20): multiple definition of `ProjectX::instance2'; CMakeFiles/Module1.dir/src/Class2.cpp.o:(.bss+0x20): first defined here /usr/bin/ld: Module2/libModule2.a(Module2.cpp.o):(.bss+0x0): multiple definition of `ProjectX::instance3'; CMakeFiles/Module1.dir/src/Class2.cpp.o:(.bss+0x0): first defined here /usr/bin/ld: Module2/libModule2.a(Module2.cpp.o):(.bss+0x10): multiple definition of `ProjectX::instance1'; CMakeFiles/Module1.dir/src/Class2.cpp.o:(.bss+0x10): first defined here collect2: error: ld returned 1 exit status
添加NewClass.cc、NewClass.h和Common.h之前项目运行正常,现在想新增可在所有模块复用的工具功能,修改后出现上述错误,求修复方案及更佳实现方式。
更新:修改后新错误
按照建议修改后,初始错误消失,但出现新的链接错误:
/usr/bin/ld: Module1/libModule1.so: undefined reference to `ProjectX::instance1' /usr/bin/ld: Module1/libModule1.so: undefined reference to `ProjectX::NewClass::method(double, double)' /usr/bin/ld: Module3/libModule3.so: undefined reference to `ProjectX::NewClass::method(std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long, std::ratio<1l, 1000000000l> > >, double)' /usr/bin/ld: Module3/libModule3.so: undefined reference to `ProjectX::instance3' /usr/bin/ld: Module1/libModule1.so: undefined reference to `ProjectX::instance2' collect2: error: ld returned 1 exit status
解决方案
1. 修复全局变量重复定义问题
核心问题是全局变量的声明与定义混淆:extern在头文件中是声明变量,告诉编译器“这个变量在别处定义”,但你直接在extern后加初始化(= NewClass()),这就变成了定义。每个包含Common.h的源文件都会生成一份变量定义,链接时必然报重复定义错误。
修正步骤:
- 修改Common.h:仅保留
extern声明,去掉初始化:
#ifndef GLOBALS_H #define GLOBALS_H #include "NewClass.h" namespace ProjectX { // 仅声明,不定义 extern NewClass instance1; extern NewClass instance2; extern NewClass instance3; } #endif // GLOBALS_H
- 新增src/Common.cc:在其中实际定义并初始化全局变量:
#include "Common.h" namespace ProjectX { // 实际定义并初始化 NewClass instance1 = NewClass(); NewClass instance2 = NewClass(); NewClass instance3 = NewClass(); }
- 更新根目录CMakeLists.txt:把
Common.cc加入库文件列表:
add_library(${PROJECT_NAME} SHARED src/NewClass.cc src/Common.cc # 新增该文件 include/NewClass.h include/Common.h )
2. 修复未定义引用错误
新错误是因为各个模块没有链接根目录的ProjectX库,导致找不到NewClass的实现和全局实例。
修正步骤:
在每个模块的CMakeLists.txt中,添加对根目录ProjectX库的链接:
- Module1/CMakeLists.txt 和 Module2/CMakeLists.txt:
set(PROJECTX_DIR ${PROJECT_SOURCE_DIR}/../) # 查找根目录构建生成的ProjectX库(需根据实际构建路径调整PATHS) find_library(PROJECTX_LIB ${PROJECT_NAME} PATHS ${PROJECTX_DIR}/build) target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECTX_DIR}/include/ ) # 链接ProjectX库 target_link_libraries(${PROJECT_NAME} PUBLIC ${PROJECTX_LIB})
- Module3/CMakeLists.txt:
set(PROJECTX_DIR ${PROJECT_SOURCE_DIR}/../../) find_library(PROJECTX_LIB ${PROJECT_NAME} PATHS ${PROJECTX_DIR}/build) target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECTX_DIR}/include/ ) target_link_libraries(${PROJECT_NAME} PUBLIC ${PROJECTX_LIB})
更规范的做法是在根目录CMake中用
install或export配置,让子模块可以通过find_package(ProjectX)自动找到库文件和头文件路径。
3. 更佳实现方式(替代全局变量)
全局变量容易引发依赖混乱、线程安全等问题,推荐以下两种方案:
- 单例模式:把
NewClass设计成单例,通过静态方法获取实例,避免全局变量:
// NewClass.h class NewClass { public: static NewClass& getInstance1() { static NewClass instance; return instance; } static NewClass& getInstance2() { static NewClass instance; return instance; } static NewClass& getInstance3() { static NewClass instance; return instance; } // ... 其他成员方法 private: NewClass() {} // 私有构造,禁止外部实例化 ~NewClass() {} NewClass(const NewClass&) = delete; NewClass& operator=(const NewClass&) = delete; };
各模块直接调用NewClass::getInstance1()即可获取实例,无链接问题。
- 依赖注入:模块需要
NewClass实例时,通过构造函数或参数传递,而非直接依赖全局变量,提升代码可测试性和灵活性。
内容的提问来源于stack exchange,提问作者MsA
相关产品推荐
相关产品推荐

