导言

单例模式的自动释放是解决资源泄漏问题的关键,不同场景下可以选择不同的实现方式。下面详细解析四种典型的自动释放机制,包括其实现原理、代码示例及适用场景。

一、方式一:利用栈对象的生命周期进行管理

实现原理

利用栈对象 "出作用域自动析构" 的特性,将单例指针存储在栈对象中,当栈对象被销毁时,自动释放单例资源。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>

class Singleton {
private:
// 私有构造函数
Singleton() {
std::cout << "Singleton 构造函数被调用" << std::endl;
}

// 私有析构函数
~Singleton() {
std::cout << "Singleton 析构函数被调用" << std::endl;
}

// 禁止拷贝和赋值
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;

// 单例实例指针
static Singleton* instance;

// 栈对象管理类
class AutoRelease {
public:
~AutoRelease() {
if (Singleton::instance != nullptr) {
delete Singleton::instance;
Singleton::instance = nullptr;
}
}
};

public:
// 获取单例实例
static Singleton* getInstance() {
// 栈对象,生命周期结束时自动析构
static AutoRelease autoRelease;

if (instance == nullptr) {
instance = new Singleton();
}
return instance;
}
};

// 初始化静态成员
Singleton* Singleton::instance = nullptr;

int main() {
Singleton* s1 = Singleton::getInstance();
Singleton* s2 = Singleton::getInstance();

std::cout << "s1 地址: " << s1 << std::endl;
std::cout << "s2 地址: " << s2 << std::endl;

// 程序结束时,autoRelease对象析构,自动释放单例
return 0;
}

特点分析

  • 优点:实现简单,利用栈对象天然的生命周期管理特性

  • 缺点:autoRelease 对象在第一次调用 getInstance () 时创建

  • 适用场景:单线程环境,对初始化时机无特殊要求的场景

二、方式二:使用内部类与内部类静态对象

实现原理

通过内部类定义释放逻辑,同时在内部类中定义静态对象,利用全局静态对象在程序结束时自动析构的特性触发单例释放。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <iostream>

class Singleton {
private:
// 私有构造函数
Singleton() {
std::cout << "Singleton 构造函数被调用" << std::endl;
}

// 私有析构函数
~Singleton() {
std::cout << "Singleton 析构函数被调用" << std::endl;
}

// 禁止拷贝和赋值
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;

// 单例实例指针
static Singleton* instance;

// 内部释放类
class Deleter {
public:
~Deleter() {
if (Singleton::instance != nullptr) {
delete Singleton::instance;
Singleton::instance = nullptr;
}
}
};

// 内部类静态对象,程序结束时自动析构
static Deleter deleter;

public:
// 获取单例实例
static Singleton* getInstance() {
if (instance == nullptr) {
instance = new Singleton();
}
return instance;
}
};

// 初始化静态成员
Singleton* Singleton::instance = nullptr;
Singleton::Deleter Singleton::deleter;

int main() {
Singleton* s = Singleton::getInstance();
return 0;
}

特点分析

  • 优点:释放逻辑封装在内部类中,实现清晰;静态对象在程序启动时就已创建

  • 缺点:即使单例从未被使用,deleter 对象也会占用资源

  • 适用场景:需要确保释放器一定存在的场景,兼容性好(支持 C++03 及以上)

三、方式三:使用 atexit () + destroyInstance ()

实现原理

利用标准库函数atexit()注册单例释放函数,当程序正常结束时,系统会自动调用所有通过atexit()注册的函数。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
#include <cstdlib> // 包含atexit()

class Singleton {
private:
// 私有构造函数
Singleton() {
std::cout << "Singleton 构造函数被调用" << std::endl;
}

// 私有析构函数
~Singleton() {
std::cout << "Singleton 析构函数被调用" << std::endl;
}

// 禁止拷贝和赋值
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;

// 单例实例指针
static Singleton* instance;

// 销毁单例的静态函数
static void destroyInstance() {
if (instance != nullptr) {
delete instance;
instance = nullptr;
}
}

public:
// 获取单例实例
static Singleton* getInstance() {
if (instance == nullptr) {
instance = new Singleton();
// 注册销毁函数,程序结束时自动调用
atexit(destroyInstance);
}
return instance;
}
};

// 初始化静态成员
Singleton* Singleton::instance = nullptr;

int main() {
Singleton* s = Singleton::getInstance();
return 0;
}

特点分析

  • 优点:利用标准库机制,实现简单,跨平台性好

  • 缺点:atexit()注册的函数调用顺序与注册顺序相反;无法传递参数

  • 适用场景:需要与其他通过atexit()管理的资源协同释放的场景

四、方式四:线程安全的实现(atexit () + destroyInstance () + pthread_once)

实现原理

结合 POSIX 线程库的pthread_once()函数确保单例初始化的线程安全性,同时使用atexit()注册释放函数,实现线程安全的自动释放。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>
#include <cstdlib>
#include <pthread.h>

class Singleton {
private:
// 私有构造函数
Singleton() {
std::cout << "Singleton 构造函数被调用" << std::endl;
}

// 私有析构函数
~Singleton() {
std::cout << "Singleton 析构函数被调用" << std::endl;
}

// 禁止拷贝和赋值
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;

// 单例实例指针
static Singleton* instance;
// pthread_once使用的控制变量
static pthread_once_t onceControl;

// 初始化单例的静态函数
static void initInstance() {
instance = new Singleton();
// 注册销毁函数
atexit(destroyInstance);
}

// 销毁单例的静态函数
static void destroyInstance() {
if (instance != nullptr) {
delete instance;
instance = nullptr;
}
}

public:
// 获取单例实例,线程安全版本
static Singleton* getInstance() {
// pthread_once确保initInstance只被调用一次,线程安全
pthread_once(&onceControl, initInstance);
return instance;
}
};

// 初始化静态成员
Singleton* Singleton::instance = nullptr;
pthread_once_t Singleton::onceControl = PTHREAD_ONCE_INIT;

int main() {
Singleton* s = Singleton::getInstance();
return 0;
}

编译与运行

需要链接 pthread 库:g++ -o singleton singleton.cpp -lpthread

特点分析

  • 优点:绝对的线程安全,初始化过程原子化;自动释放资源

  • 缺点:依赖 POSIX 线程库,Windows 平台需要特殊处理

  • 适用场景:Linux/Unix 多线程环境,对线程安全性要求高的场景

五、五种自动释放方式对比表

实现方式 线程安全 兼容性 实现复杂度 释放时机控制 资源占用
栈对象管理
内部类静态对象
atexit () 方式
pthread_once 方式