首页 > Chrome源码学习, 设计模式 > C++单例模式Singleton内存回收

C++单例模式Singleton内存回收

单例模式(Singleton)也称为单件模式,其意图是保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。有很多地方需要这样的功能模块,如系统的日志输出,GUI应用必须是单鼠标,操作系统只会弹出一个任务管理器等。

单例模式有许多种实现方法,在C++中,甚至可以直接用一个全局变量做到这一点,但这样的代码显的很不优雅。 使用全局对象能够保证方便地访问实例,但是不能保证只声明一个对象——也就是说除了一个全局实例外,仍然能创建相同类的本地实例。

GoF的《设计模式》一书中给出了一种很不错的实现,定义一个单例类,使用类的私有静态指针变量指向类的唯一实例,并用一个公有的静态方法获取该实例。

单例模式通过类本身来管理其唯一实例,这种特性提供了解决问题的方法。唯一的实例是类的一个普通对象,但设计这个类时,让它只能创建一个实例并提供 对此实例的全局访问。唯一实例类Singleton在静态成员函数中隐藏创建实例的操作。习惯上把这个成员函数叫做Instance(),它的返回值是唯 一实例的指针。一个单例模式的源码可能如下:

// Singleton.h
#ifndef _CSINGLETON_H_
#define _CSINGLETON_H_
 
class CSingleton
{
private:
	CSingleton() {};
	static CSingleton* _instance;
 
public:
	static CSingleton* GetInstance();
 
// ... other members or functions
public:
	void DoSomething();
};
 
#endif // _CSINGLETON_H_
 
// Singleton.cpp
#include "Singleton.h"
#include <windows.h>
#include <stdio.h>
 
CSingleton* CSingleton::_instance = NULL;
 
CSingleton* CSingleton::GetInstance()
{
	if (_instance == NULL)
	{
		_instance = new CSingleton();
	}
 
	return _instance;
}
 
void CSingleton::DoSomething()
{
	printf("void CSingleton::DoSomething() called.\n");
}
 
// 调用单例模式Singleton类
CSingleton::GetInstance()->DoSomething();

这里会有一个问题,_instance实例是new出来的,它什么时候会被delete掉?CSingleton的析构函数什么时候调用?我们可以模仿《MSVC CRT运行库启动代码分析》中提到的利用atexit注册一个回收函数,这样程序退出时自动delete,不过多少有点不完美,万一多注册了几次,那程序就崩溃了!

我们可以在Singleton类中定义一个内部类Deleter,如下:

class CSingleton
{
private:
	CSingleton() {};
	static CSingleton* _instance;
 
public:
	static CSingleton* GetInstance();
 
// ... other members or functions
public:
	void DoSomething();
 
private:
	class Deleter
	{
	public:
		~Deleter()
		{
			if (CSingleton::_instance != NULL)
			{
				delete CSingleton::_instance;
			}
		}
	};
	// 定义一个静态的Deleter实例
	static Deleter deleter;
};

Singleton拥有一个静态的Deleter成员,这个成员在离开其作用域时会自动调用析构函数,而Deleter的析构函数就是负责对Singleton实例进行删除。

还有另一个好一点的解决方案,就是使用静态的成员变量实例,但是要处理复制的问题:

#define DISALLOW_COPY(TypeName) \
	TypeName(const TypeName&)
 
#define DISALLOW_ASSIGN(TypeName) \
	void operator=(const TypeName&)
 
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
	TypeName(const TypeName&);               \
	void operator=(const TypeName&)
 
class CSingleton
{
public:
	static CSingleton &GetInstance()
	{
		static CSingleton instance;
		return instance;
	}
	void DoSomething()
	{
		printf("void CSingleton::DoSomething() called.\n");
	}
 
private:
	CSingleton() {};
	DISALLOW_COPY_AND_ASSIGN(CSingleton);
};
 
// 单例模式类使用
CSingleton::GetInstance().DoSomething();	// OK
CSingleton singleton = CSingleton::GetInstance(); // ERROR 不能通过编译

这里对复制构造函数和赋值操作符进行了处理,可以保证只存在一个实例,而且不用考虑内存回收的问题。

在Chrome中,定义了一个AtExitManager类,用于负责类的内存回收问题。

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// at_exit.h
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
 
#ifndef BASE_AT_EXIT_H_
#define BASE_AT_EXIT_H_
 
#include <stack>
 
#include "base/base_export.h"
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/synchronization/lock.h"
 
namespace base {
 
// This class provides a facility similar to the CRT atexit(), except that
// we control when the callbacks are executed. Under Windows for a DLL they
// happen at a really bad time and under the loader lock. This facility is
// mostly used by base::Singleton.
//
// The usage is simple. Early in the main() or WinMain() scope create an
// AtExitManager object on the stack:
// int main(...) {
//    base::AtExitManager exit_manager;
//
// }
// When the exit_manager object goes out of scope, all the registered
// callbacks and singleton destructors will be called.
 
class BASE_EXPORT AtExitManager {
 public:
  typedef void (*AtExitCallbackType)(void*);
 
  AtExitManager();
 
  // The dtor calls all the registered callbacks. Do not try to register more
  // callbacks after this point.
  ~AtExitManager();
 
  // Registers the specified function to be called at exit. The prototype of
  // the callback function is void func(void*).
  static void RegisterCallback(AtExitCallbackType func, void* param);
 
  // Registers the specified task to be called at exit.
  static void RegisterTask(base::Closure task);
 
  // Calls the functions registered with RegisterCallback in LIFO order. It
  // is possible to register new callbacks after calling this function.
  static void ProcessCallbacksNow();
 
 protected:
  // This constructor will allow this instance of AtExitManager to be created
  // even if one already exists.  This should only be used for testing!
  // AtExitManagers are kept on a global stack, and it will be removed during
  // destruction.  This allows you to shadow another AtExitManager.
  explicit AtExitManager(bool shadow);
 
 private:
  base::Lock lock_;
  std::stack<base::Closure> stack_;
  AtExitManager* next_manager_;  // Stack of managers to allow shadowing.
 
  DISALLOW_COPY_AND_ASSIGN(AtExitManager);
};
 
#if defined(UNIT_TEST)
class ShadowingAtExitManager : public AtExitManager {
 public:
  ShadowingAtExitManager() : AtExitManager(true) {}
};
#endif  // defined(UNIT_TEST)
 
}  // namespace base
 
#endif  // BASE_AT_EXIT_H_
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// at_exit.cc
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
 
#include "base/at_exit.h"
 
#include <stddef.h>
#include <ostream>
 
#include "base/bind.h"
#include "base/callback.h"
#include "base/logging.h"
 
namespace base {
 
// Keep a stack of registered AtExitManagers.  We always operate on the most
// recent, and we should never have more than one outside of testing (for a
// statically linked version of this library).  Testing may use the shadow
// version of the constructor, and if we are building a dynamic library we may
// end up with multiple AtExitManagers on the same process.  We don't protect
// this for thread-safe access, since it will only be modified in testing.
static AtExitManager* g_top_manager = NULL;
 
AtExitManager::AtExitManager() : next_manager_(g_top_manager) {
// If multiple modules instantiate AtExitManagers they'll end up living in this
// module... they have to coexist.
#if !defined(COMPONENT_BUILD)
  DCHECK(!g_top_manager);
#endif
  g_top_manager = this;
}
 
AtExitManager::~AtExitManager() {
  if (!g_top_manager) {
    NOTREACHED() << "Tried to ~AtExitManager without an AtExitManager";
    return;
  }
  DCHECK_EQ(this, g_top_manager);
 
  ProcessCallbacksNow();
  g_top_manager = next_manager_;
}
 
// static
void AtExitManager::RegisterCallback(AtExitCallbackType func, void* param) {
  DCHECK(func);
  RegisterTask(base::Bind(func, param));
}
 
// static
void AtExitManager::RegisterTask(base::Closure task) {
  if (!g_top_manager) {
    NOTREACHED() << "Tried to RegisterCallback without an AtExitManager";
    return;
  }
 
  AutoLock lock(g_top_manager->lock_);
  g_top_manager->stack_.push(task);
}
 
// static
void AtExitManager::ProcessCallbacksNow() {
  if (!g_top_manager) {
    NOTREACHED() << "Tried to ProcessCallbacksNow without an AtExitManager";
    return;
  }
 
  AutoLock lock(g_top_manager->lock_);
 
  while (!g_top_manager->stack_.empty()) {
    base::Closure task = g_top_manager->stack_.top();
    task.Run();
    g_top_manager->stack_.pop();
  }
}
 
AtExitManager::AtExitManager(bool shadow) : next_manager_(g_top_manager) {
  DCHECK(shadow || !g_top_manager);
  g_top_manager = this;
}
 
}  // namespace base

AtExitManager模仿的就是atexit函数的功能,使用的时候,可以把WinMain函数中定义一个AtExitManager实例:

base::AtExitManager exit_manager;

之后,在任何地方,只需要调用RegisterCallback函数注册回调函数即可。可以定义多个AtExitManager实例,其内部会有一个链表维护这些实例。
单例模式类图
参考:
C++单例实现及回收


觉得文章还不错?点击此处对作者进行打赏!


本文地址: 程序人生 >> C++单例模式Singleton内存回收
作者:代码疯子(Wins0n) 本站内容如无声明均属原创,转载请保留作者信息与原文链接,谢谢!


更多



  1. 2013年11月12日23:34 | #1

    [em019] 以前用的都是Java的单例,没有考虑过C++上面的内存回收,受教了!

    [回复]

  2. 萧叶
    2014年3月9日09:34 | #2

    关于第二段处理一防止对象拷贝复制的时候,不用对拷贝构造函数进行私有化或者是其他处理,只要私有化析构函数好像就可以了。

    [回复]

    代码疯子 回复:

    @萧叶, 这样不行吧,那析构函数都没法调用了。

    [回复]

  1. 本文目前尚无任何 trackbacks 和 pingbacks.