C++ inline 单例函数在 Windows DLL 中的多实例问题
·
- 根据 C++ 标准 ODR 规则,
inline函数内的静态局部变量经多翻译单元链接合并后,全局仅存在唯一实例。 - Windows DLL 机制会破坏该标准语义:可执行文件(EXE)与动态链接库(DLL)分属独立模块,拥有隔离的符号寻址空间;链接器不会跨模块合并
inline函数内的静态局部变量,每个模块会各自生成一份独立变量副本。 - 该特性会严重破坏基于
inline静态局部变量实现的单例模式,丢失 “全局唯一实例” 核心约束,极易引发实例未初始化、多模块读写不同实例、程序异常崩溃等未定义行为。
最小复现案例
singleton.h
#pragma once
#include <string>
#include <iostream>
class Singleton {
public:
// ❌ 问题:inline 定义在头文件中
static Singleton& instance() {
static Singleton obj; // EXE 和 DLL 各有一份
return obj;
}
void set_name(const std::string& name) {
name_ = name;
}
const std::string& name() const {
return name_;
}
void print() {
std::cout << "Singleton address: " << this << std::endl;
std::cout << "name = \"" << name_ << "\"" << std::endl;
}
private:
Singleton() = default;
std::string name_{ "<未初始化>" };
};
test.cpp
#include "singleton.h"
#include <iostream>
// 导出函数,供 EXE 调用
__declspec(dllexport) void dll_use_singleton() {
std::cout << "--- DLL 侧访问单例 ---" << std::endl;
Singleton::instance().print();
// 此时 name_ 仍然是 "<未初始化>",因为 DLL 侧是另一个实例
}
main.cpp
#include "singleton.h"
#include <iostream>
#include <windows.h>
__declspec(dllimport) void dll_use_singleton();
int main() {
SetConsoleOutputCP(CP_UTF8);
std::cout << "--- EXE 侧设置单例 ---" << std::endl;
Singleton::instance().set_name("Hello from EXE");
Singleton::instance().print();
std::cout << std::endl;
// 调用 DLL 函数,DLL 内部也访问单例
dll_use_singleton();
return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(singleton_test LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
if(MSVC)
add_compile_options(/utf-8)
endif()
# 构建 DLL
add_library(my_dll SHARED test.cpp)
# 构建 EXE
add_executable(main_app main.cpp)
target_link_libraries(main_app PRIVATE my_dll)
程序运行结果:
--- EXE 侧设置单例 ---
Singleton address: 00007FF637CD3518
name = "Hello from EXE"
--- DLL 侧访问单例 ---
Singleton address: 00007FFBD64D3508
name = "<未初始化>"
可以看到,单例地址不同,这说明主函数中访问的单例与dll访问的单例并不是同一个。
修复方案
将单例的声明与实现分开,singleton.h修改为:
#pragma once
#include <string>
#include <iostream>
#ifdef my_dll_EXPORTS
#define SINGLETON_API __declspec(dllexport)
#else
#define SINGLETON_API __declspec(dllimport)
#endif
class SINGLETON_API Singleton {
public:
static Singleton& instance();
void set_name(const std::string& name) {
name_ = name;
}
const std::string& name() const {
return name_;
}
void print() {
std::cout << "Singleton address: " << this << std::endl;
std::cout << "name = \"" << name_ << "\"" << std::endl;
}
private:
Singleton() = default;
std::string name_{ "<未初始化>" };
};
添加singleton.cpp:
#include "singleton.h"
Singleton& Singleton::instance() {
static Singleton obj;
return obj;
}
同时修改CMakeLists.txt为:
cmake_minimum_required(VERSION 3.14)
project(singleton_test LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
if(MSVC)
add_compile_options(/utf-8)
endif()
# 构建 DLL
add_library(my_dll SHARED test.cpp singleton.cpp)
# 构建 EXE
add_executable(main_app main.cpp)
target_link_libraries(main_app PRIVATE my_dll)
程序运行结果:
--- EXE 侧设置单例 ---
Singleton address: 00007FFBBBD84508
name = "Hello from EXE"
--- DLL 侧访问单例 ---
Singleton address: 00007FFBBBD84508
name = "Hello from EXE"
可以看到,此时单例全局仅存在一份。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)