单例的核心结构: 私有的构造函数,保证只能自己内部初始化实例
一个静态私有的实例对象,占用内存中唯一的实例的位置,后续访问都是访问它
一个静态公有的访问器:public static Classname getInstance(); 唯一能访问到单例的对外的接口
类加载到内存中的时候,实例化一个类的单例;
简单使用,推荐,它是线程安全的,因为只有在加载的时候可以调用到构造函数
唯一缺点: 不管用到预购,类加载的时候都会完成该单例的实例化。不用也会占用内存。
头文件:
#pragma once
class Simple_SingleInstance
{
private :
Simple_SingleInstance();
static Simple_SingleInstance *instance;;
public:
static Simple_SingleInstance* getInstance();
};
源文件:
#include "Simple_SingleInstance.h"
Simple_SingleInstance* Simple_SingleInstance::instance = new Simple_SingleInstance();
Simple_SingleInstance :: Simple_SingleInstance()
{
}
Simple_SingleInstance* Simple_SingleInstance::getInstance()
{
return instance;
}
主函数:
#include<iostream>
#include"Simple_SingleInstance.h"
int main()
{
std::cout << "Hello World!" << std::endl;
Simple_SingleInstance* p1 = Simple_SingleInstance::getInstance();
Simple_SingleInstance * p2 = Simple_SingleInstance::getInstance();
if (p1 == p2)
{
std::cout << "单例模式成功" << std::endl;
}
return 0;
}运行结果:
在主函数执行之前,会先进入这里,类加载的时候,就初始化了单例的对象


在第一次使用的时候进行单例的初始化;可能带来线程的安全问题。
头文件:
#pragma once
class SingleInstance2
{
private:
SingleInstance2();
static SingleInstance2 * m_pInstance;
public:
static SingleInstance2* GetInstance();
}; 源文件:
#include "SingleInstance2.h"
SingleInstance2* SingleInstance2::m_pInstance = nullptr;
SingleInstance2::SingleInstance2()
{
}
SingleInstance2* SingleInstance2::GetInstance()
{
//有两个线程同时访问的时候,在一个线程通过了这个为空的判断,但还没来得及实例出对象,另一个线程也进入
//此处的判断,从而先后实例化出两个对象,破坏了单例的唯一性,在两个线程中使用的是两个不同的对象。
if(m_pInstance == nullptr)
m_pInstance = new SingleInstance2();
return m_pInstance;
}
主函数和运行结果:
#include<iostream>
#include"SingleInstance2.h"
#include<vector>
#include<thread>
void threadFunction(int id)
{
SingleInstance2 * instance = SingleInstance2::GetInstance();
std::cout << "Thread " << id << " got instance at address: "
<< instance << std::endl;
}
int main()
{
const int NUN_THREADS = 100;
std::vector<std::thread> threads;
for (int i = 0; i < NUN_THREADS; i++)
{
// 使用 push_back - 需要先创建对象再移动/拷贝
// threads.push_back(std::thread(threadFunction, i));
// 使用 emplace_back - 直接在容器内构造对象
threads.emplace_back(threadFunction, i);
/* • 性能更好: 避免不必要的拷贝或移动操作
• 代码更简洁: 参数直接传递给构造函数
• 避免临时对象: 直接在容器分配的内存中构造 */
}
return 0;
}运行结果: 你会发现 多线程创建的访问的instance的地址是不同的

解决懒汉式单例线程安全性的方法:
先不说C++的实现方法,先说说思路,加锁(参考java)
1.加锁的位置:对方法加锁,这种方法虽然达到了线程安全的目的,但是多个线程要排队等取锁才能进入这个函数,效率就降低了,所以不是很推荐
public static synchronized Singleton getInstance() {
if (null == INSTANCE) {
try {
//模拟延迟,多线程下同时进入此代码块。
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Singleton();
}
return INSTANCE;
}2.加锁的位置,只对创建的代码块加锁,但使用双重检查。
如果不适用双重检查带来的问题是:第一个线程通过为空检查后,第二个也进入了为空检查,第二个线程拿到锁,创建了对象,释放锁给第一个线程,第一个线程拿到锁还是会在此进入代码块创建一个新对象。使用双重检查可以避免你在上锁期间有人插队创建对象,你上锁之后再次检查对象是否为空。
双重检查锁虽然代码比较复杂,但是相对来说是比较完美的。
public static Singleton getInstance() {
if (null == INSTANCE) {
synchronized (Singleton.class) {
if (null == INSTANCE) {
try {
//模拟延迟,多线程下同时进入此代码块。
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}java中还有两种更好的写法:
私有的静态内部类 (类加载的时候不会加载内部静态类,在第一次调用的时候才会被加载)
枚举单例(枚举方法没有构造函数,所以不会被反序列化)
C++实现懒汉单例的方法:
1.静态局部变量的懒汉式单例(推荐, 线程安全)
头文件:
#pragma once
class SingleInstance3
{
private:
//使用默认构造函数和析构函数 禁止外部构造和外部析构
SingleInstance3() = default;
~SingleInstance3() = default;
//禁止外部拷贝构造
SingleInstance3(const SingleInstance3&) = delete;
//禁止外部赋值运算符
const SingleInstance3& operator = (const SingleInstance3&) = delete;
public:
static SingleInstance3* getInstance();
};源文件:
#include "SingleInstance3.h"
SingleInstance3* SingleInstance3::getInstance()
{
/**
*
*局部静态变量的方式实现单例
*局部静态变量只在当前函数内部有效,其他函数无法访问
*静态局部遍历只在第一次调用的时候初始化,存放在静态存储区,生命周期从第一次被创建到程序结束
*/
static SingleInstance3 instance;
return &instance;
}2.使用unique_lock 和mutex实现双重锁
头文件:
#pragma once
#include<mutex>
class SingleInstance4
{
private:
//私有构造函数,防止外部实例化
SingleInstance4() = default;
//私有析构函数,防止外部删除
~SingleInstance4() = default;
//拷贝构造函数和赋值构造设为私有,防止外部拷贝赋值
SingleInstance4(const SingleInstance4&) =delete;
const SingleInstance4& operator = (const SingleInstance4&) = delete;
private:
static SingleInstance4* m_pInstance;
static std::mutex m_mutex;
public:
static SingleInstance4* getInstance();
/**
* 如果使用智能指针, m_pInstance应定义为
* std::shared_ptr<SingleInstance4> m_pInstance;
static std::shared_ptr<SingleInstance4> getInstance();
*/
};
源文件:
#include "SingleInstance4.h"
// 静态成员变量定义 - 关键步骤!
SingleInstance4* SingleInstance4::m_pInstance = nullptr;
std::mutex SingleInstance4::m_mutex ;
SingleInstance4* SingleInstance4::getInstance()
{
// 第一次检查(不加锁) 可以使大部分线程跳过下面的过程 节约时间
if (m_pInstance == nullptr)
{
// 使用 unique_lock 加锁
std::unique_lock<std::mutex> lock(m_mutex);
if (m_pInstance == nullptr)
{
volatile auto temp = new (std::nothrow)SingleInstance4();
m_pInstance = temp;
}
}
return m_pInstance;
}3.使用std::call_once实现懒汉单例(线程安全)
头文件:
#pragma once
#include<mutex>
class SingleInstance4
{
private:
//私有构造函数,防止外部实例化
SingleInstance4() = default;
//私有析构函数,防止外部删除
~SingleInstance4() = default;
//拷贝构造函数和赋值构造设为私有,防止外部拷贝赋值
SingleInstance4(const SingleInstance4&) =delete;
const SingleInstance4& operator = (const SingleInstance4&) = delete;
private:
static SingleInstance4* m_pInstance;
static std::once_flag single_once_flag;
public:
static SingleInstance4* getInstance();
/**
* 如果使用智能指针, m_pInstance应定义为
* std::shared_ptr<SingleInstance4> m_pInstance;
static std::shared_ptr<SingleInstance4> getInstance();
*/
};
源文件:
#include "SingleInstance4.h"
// 静态成员变量定义 - 关键步骤!
SingleInstance4* SingleInstance4::m_pInstance = nullptr;
std::once_flag SingleInstance4::single_once_flag;
SingleInstance4* SingleInstance4::getInstance()
{
// 第一次检查(不加锁) 可以使大部分线程跳过下面的过程 节约时间
if (m_pInstance == nullptr)
{
// 使用call_once(falg,[&](){ 原子操作 }
std::call_once(single_once_flag, [&]() {
// 使用 nothrow:内存分配失败时返回 nullptr
m_pInstance = new (std::nothrow)SingleInstance4();
});
}
return m_pInstance;
}原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。