一、定义
确保一个类只有一个实例, 并提供一个访问该实例的全局节点。
二、要解决的问题
- 单一实例
无论new了多少个对象,始终只存在一个实例 - 应用场景
对临界资源(例如日志、打印机)的访问
三、解决步骤
- 将默认构造函数声明成私有类型,防止其他对象使用单例类的
new
运算符 - 新建一个静态构建方法作为构造函数。
- 该静态函数会调用私有构造函数来创建对象, 并将其保存在一个静态成员变量中。 此后所有对于该函数的调用都将返回这一缓存对象。
四、应用场景
- 如果程序中的某个类对于所有客户端只有一个可用的实例,使用单例模式
- 单例模式
禁止
通过除特殊构建方法以外的任何方式来创建自身类的对象。 - 该方法可以创建一个新对象, 但如果该对象已经被创建, 则返回已有的对象。
- 单例模式
- 如果需要更加严格地控制全局变量,可以使用单例模式
- 单例模式与全局变量不同,它保证类只存在一个实例。
- 除了单例模式以外,无法通过任何方式替换缓存的实例
五、代码实现
4.1 实现方法1
下面这种方式存在以下问题
- 只提供了
getInstance
方法获取对象,没有提供释放函数 - 析构函数不会被运行,可以使用智能指针替代原指针
- 多线程调用
getInstance
时将使用锁
来保证安全
#include <iostream>
#include <mutex>
#include <thread>
class Singleton
{
private:
static Singleton* m_singleton; //实例对象为私有
static std::mutex m_mutex;
Singleton() //构造方法为私有
{
std::cout << "Singleton" << std::endl;
}
~Singleton()
{
std::cout << "~Singleton" << std::endl;
}
//禁用拷贝构造函数和赋值运算符
Singleton(const Singleton& obj) = delete;
Singleton& operator=(const Singleton& obj) = delete;
public:
static Singleton* getInstance()
{
std::lock_guard<std::mutex> lock(m_mutex); //添加线程锁
if (!m_singleton)
{
m_singleton = new Singleton();
}
return m_singleton;
}
void printAddress(const std::string& preString)
{
printf("%s: %p\n", preString.c_str(), this);
}
};
Singleton* Singleton::m_singleton = nullptr;
std::mutex Singleton::m_mutex;
void ThreadFunc()
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
Singleton* single = Singleton::getInstance();
single->printAddress("ThreadFunc");
}
void ThreadFunc2()
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
Singleton* single = Singleton::getInstance();
single->printAddress("ThreadFunc2");
}
int main()
{
std::thread t1(ThreadFunc);
std::thread t2(ThreadFunc2);
t1.join();
t2.join();
return 0;
}
4.2 实现方法2
改进点
- 采用
局部静态变量
的方式返回,线程安全(c++11及以后) - 没有采用new关键字在堆空间中申请内存,空间被自动管理
- 析构函数被自动执行
- 下面是
Singleton
类的实现,其它函数保持原样
#include <iostream>
#include <mutex>
#include <thread>
class Singleton
{
private:
Singleton() //构造方法为私有
{
std::cout << "Singleton" << std::endl;
}
~Singleton()
{
std::cout << "~Singleton" << std::endl;
}
//禁用拷贝构造函数和赋值运算符
Singleton(const Singleton& obj) = delete;
Singleton& operator=(const Singleton& obj) = delete;
public:
//返回指针可能被误delete,直接返回引用
static Singleton& getInstance()
{
static Singleton m_signleton;
return m_signleton;
}
void printAddress(const std::string& preString)
{
std::cout << preString << " : " << this << std::endl;
}
};
void ThreadFunc()
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
Singleton& single = Singleton::getInstance();
single.printAddress("ThreadFunc");
}
void ThreadFunc2()
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
Singleton& single = Singleton::getInstance();
single.printAddress("ThreadFunc2");
}
int main()
{
std::thread t1(ThreadFunc);
std::thread t2(ThreadFunc2);
t1.join();
t2.join();
return 0;
}