设计模式之单例模式

简介: 设计模式之单例模式


单例模式

定义

保证一个类仅有一个实例,并提供一个该实例的全局访问点。 ——《设计模式》GoF

代码–版本1

// 内存栈区
// 内存堆区
// 常数区
// 静态区 系统释放
// 二进制代码区
class Singleton {
public:
static Singleton * GetInstance() {
if (_instance == nullptr) {
_instance = new Singleton();
}
return _instance;
}
private:
Singleton(){}//构造
Singleton(const Singleton &clone){} //拷⻉构造
Singleton& operator=(const Singleton&) {}//可能会赋值,也要设置成private
static Singleton * _instance;//变量需要声明为静态
}
Singleton* Singleton::_instance = nullptr;//静态成员需要初始化
版本1的问题与解决技巧
  • 多线程有问题1
  • 单线程内存泄漏,内存释放有问题2
  • 静态区是系统帮我们释放内存,系统会帮我们释放静态区_instance的指针,但不会释放内存堆区的_instance = new Singleton(),new的对象
  • 如果说加入一个析构函数,那么谁来调用析构函数?

代码–版本2

class Singleton {
public:
static Singleton * GetInstance() {
if (_instance == nullptr) {
_instance = new Singleton();
atexit(Destructor);
}
return _instance;
}
~Singleton() {}
private:
static void Destructor() {//private保护
if (nullptr != _instance) {
delete _instance;
_instance = nullptr;
}
}
Singleton();//构造
Singleton(const Singleton &cpy); //拷⻉构造
Singleton& operator=(const Singleton&) {}
static Singleton * _instance;
}
Singleton* Singleton::_instance = nullptr;//静态成员需要初始化
// 还可以使用 内部类,智能指针来解决; 此时还有线程安全问题
//public:
//  class GC {
//    ~GC(){
//      delete _instance
//};
版本2的问题与解决技巧
  • 程序退出的时候atexit(Destructor)可帮我们解决内存释放问题
  • 内部类方法可以考虑加一个static GC gc对象 ,初始化Singleton::GC gc ;gc对象也存储在静态区系统自动释放,它释放时系统会调用~GC()析构函数
  • 借助静态区解决了堆内存释放,对象可以自己调用析构函数指针要手动调用还是要思考多线程问题

代码–版本3

#include <mutex>
class Singleton { // 懒汉模式 lazy load
public:
static Singleton * GetInstance() {//std::lock_guard<std::mutex> lock(_mutex); // 3.1 切换线程   
if (_instance == nullptr) {
std::lock_guard<std::mutex> lock(_mutex); // 3.2
if (_instance == nullptr) {
_instance = new Singleton();
atexit(Destructor);
}
}
return _instance;
}
private:
static void Destructor() {
if (nullptr != _instance) {
delete _instance;
_instance = nullptr;
}
}
Singleton(){} //构造
Singleton(const Singleton &cpy){} //拷⻉构造
Singleton& operator=(const Singleton&) {}
static Singleton * _instance;
static std::mutex _mutex;
}
Singleton* Singleton::_instance = nullptr;//静态成员需要初始化
std::mutex Singleton::_mutex; //互斥锁初始化
版本3的问题与解决技巧
  • 3.1 处互斥锁有什么问题?系统会切换线程锁力度大,系统调用开销大,我们只是读_instance这把锁应该要恰到时机只需要在_instance = new Singleton();new对象时加锁 问题1
  • new分为三个步骤:1.分配内存 2.调用构造函数 3.赋值操作 多线程环境下cpu进行重排,cpu指令reorder 问题2
  • 加锁为什么需要双检测?不是一个原子操作晚到的线程_instance 不等于nullptr 就直接进行返回了,可能没有调用构造函数:如不希望的顺序CPU执行顺序1 3, 2
  • 问题3多线程双检测后到的线程有问题
  • coredump不是空指针,类成员变量 奇怪的值
  • 3.1没有3.2的问题但是3.1效率低
  • once_flag里init实现

代码–版本4

#include <mutex>
#include <atomic>
class Singleton {
public:
static Singleton * GetInstance() {
Singleton* tmp = _instance.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);//获取内存屏障
if (tmp == nullptr) {
std::lock_guard<std::mutex> lock(_mutex);
tmp = _instance.load(std::memory_order_relaxed);
if (tmp == nullptr) {
tmp = new Singleton;
std::atomic_thread_fence(std::memory_order_release);//释放内存屏
_instance.store(tmp, std::memory_order_relaxed);
atexit(Destructor);
}
}
return tmp;
}private:
static void Destructor() {
Singleton* tmp = _instance.load(std::memory_order_relaxed);
if (nullptr != tmp) {
delete tmp;
}
}
Singleton(){}
Singleton(const Singleton&) {}
Singleton& operator=(const Singleton&) {}
static std::atomic<Singleton*> _instance;
static std::mutex _mutex;
};
std::atomic<Singleton*> Singleton::_instance;//静态成员需要初始化
std::mutex Singleton::_mutex; //互斥锁初始化
// g++ Singleton.cpp -o singleton -std=c++11
版本4的问题与解决技巧
  • 解决内存reorder操作
  • 内存屏障解决cpu 指令reorder
  • java 里面有valitile
  • 版本1.2.3是懒汉模式这样不会在一启动的时候就加载过多的内存

代码–版本5

// c++11 magic static 特性:如果当变量在初始化的时候,并发同时进入声明语句,并发
线程将会阻塞等待初始化结束。
class Singleton
{
public:
~Singleton(){}
static Singleton& GetInstance() {
static Singleton instance;
return instance;
}
private:
Singleton(){}
Singleton(const Singleton&) {}
Singleton& operator=(const Singleton&) {}
};
// 继承 Singleton
// g++ Singleton.cpp -o singleton -std=c++11
/*该版本具备 版本5 所有优点:
 - 利用静态局部变量特性,延迟加载;
 - 利用静态局部变量特性,系统自动回收内存,自动调用析构函数;
 - 静态局部变量初始化时,没有 new 操作带来的cpu指令reorder操作;
 - c++11 静态局部变量初始化时,具备线程安全;
*/
版本5的问题与解决技巧
  • 静态局部变量
  • 懒汉模式
  • 系统自动释放
  • c++11 static自带线程安全
  • 那问题来了怎么继承?构造函数私有怎么继承嘞

代码–版本6

template<typename T>
class Singleton {
public:static T& GetInstance() {
static T instance; // 这里要初始化DesignPattern,需要调用
//DesignPattern 构造函数,同时会调用父类的构造函数。
return instance;
}
protected:
virtual ~Singleton() {}
Singleton() {} // protected修饰构造函数,才能让别人继承
Singleton(const Singleton&) {}
Singleton& operator =(const Singleton&) {}
};
class DesignPattern : public Singleton<DesignPattern> {
friend class Singleton<DesignPattern>; // friend 能让 Singleton<T> 访
//问到 DesignPattern构造函数
private:
DesignPattern(){}
DesignPattern(const DesignPattern&) {}
DesignPattern& operator=(const DesignPattern&) {}
}
版本6的问题与解决技巧
  • 能不能在Singleton中修改protected为private并加入friend class DisignPattern呢?可以但是不能这样做子类拓展性不好了,不然每一个子类都要这样做
  • 非常巧妙protected

要点

  • 变化的点应该依赖于稳定的点
  • 封装变化点,识别出稳定点与变化点
  • 单一职责原则
  • 掌握比较深入

结构图

相关文章
|
2月前
|
设计模式 存储 前端开发
前端必须掌握的设计模式——单例模式
单例模式是一种简单的创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点。适用于窗口对象、登录弹窗等场景,优点包括易于维护、访问和低消耗,但也有安全隐患、可能形成巨石对象及扩展性差等缺点。文中展示了JavaScript和TypeScript的实现方法。
|
2月前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
40 2
|
3月前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
|
3月前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
35 0
|
3月前
|
设计模式 Java 数据库连接
Java编程中的设计模式:单例模式的深度剖析
【10月更文挑战第41天】本文深入探讨了Java中广泛使用的单例设计模式,旨在通过简明扼要的语言和实际示例,帮助读者理解其核心原理和应用。文章将介绍单例模式的重要性、实现方式以及在实际应用中如何优雅地处理多线程问题。
56 4
|
3月前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
|
3月前
|
设计模式 存储 数据库连接
PHP中的设计模式:单例模式的深入理解与应用
【10月更文挑战第22天】 在软件开发中,设计模式是解决特定问题的通用解决方案。本文将通过通俗易懂的语言和实例,深入探讨PHP中单例模式的概念、实现方法及其在实际开发中的应用,帮助读者更好地理解和运用这一重要的设计模式。
33 1
|
4月前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
34 0
|
4月前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
本教程详细讲解了Kotlin中的单例模式实现,包括饿汉式、懒汉式、双重检查锁、静态内部类及枚举类等方法,适合需要深入了解Kotlin单例模式的开发者。快速学习者可参考“简洁”系列教程。
55 0
|
4月前
|
设计模式 存储 数据库连接
Python编程中的设计模式之美:单例模式的妙用与实现###
本文将深入浅出地探讨Python编程中的一种重要设计模式——单例模式。通过生动的比喻、清晰的逻辑和实用的代码示例,让读者轻松理解单例模式的核心概念、应用场景及如何在Python中高效实现。无论是初学者还是有经验的开发者,都能从中获得启发,提升对设计模式的理解和应用能力。 ###

热门文章

最新文章