单例模式 - 对象性能

单例模式提供了一种创建对象的最佳方式,这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其对象的唯一方式,可以直接访问,不需要实例化该类的对象。

注意:1、单例只能有一个实例。2、单例类必须自己创建自己唯一的实例。3、单例类必须给所有的其他对象提供这一实例。

1. 介绍

意图:保证一个类只有一个实例,并提供一个访问他的全局访问点。

主要解决:一个全局使用的类平凡地创建与销毁。

何时使用:当您想控制实例数目,节省系统资源的时候。

如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

关键代码:构造函数是私有的

应用实例:1、一个班只有一个班主任

2、window是多进程多线程的,在操作一个文件的时候,就不避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须同故宫唯一的实例来进行。

3、一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,再输出的时候就要处理不能两台打印机打印同一个文件。

优点:1、在内存里是有一个实例,减少了内存的开销,尤其是平凡的出案件和销毁实例(比如管理学院网页首页缓存)。2、避免对资源的多重占用(比如写文件操作)。

缺点:没有借口,不能继承,与单一职责原则冲突,一个类应该之关系内部逻辑,而不关心外部怎么样来实例化。

使用场景:

  1. 要求生产唯一序列号
  2. WEB中的计数器,不用每次刷新都在数据库离家再一次,用单例先缓存起来。
  3. 创建一个对象需要消耗的资源过多,比如I/O与数据库的连接等。

2. 实现

2.1. C++实现

注意事项:getinstance()方法中需要使用同步锁防止多线程同时进入导致instance被多次实例化。

我们将创建一个 SingleObject 类。SingleObject 类有它的私有构造函数和本身的一个静态实例。

SingleObject 类提供了一个静态方法,供外界获取它的静态实例。SingletonPatternDemo 类使用 SingleObject 类来获取 SingleObject 对象。

#include <stdio.h>
#include <mutex>
#include <vector>

template < typename T > singleton{
public:
	singleton(){};
	~singleton(){};

	static T* GetInstance() {
		if (m_pInstance == nullptr)
		{
          	std::lock_guard <std::mutex> m_mutex;    // 双检查锁
			if (m_pInstance == nullptr)
			{
				m_pInstance =  new T();
			}
        }
		return m_pInstance;
    }
    
    /* 比较简单粗暴的释放方式 */
    static void free( void )
    {
        if( NULL != m_pInstance )
        {
            delete m_pInstance;
        }
    }

    class{
    public:
        ~Garbo()
        {
            if(singleton::m_pInstance != NULL)
            {
                delete m_pInstance;
            }
        }

    }

	static std::mutex m_mutex;
	static T* m_pInstance;
    /* 利用对象确定析构的原则进行内存释放 */
    static Garbo m_garbo;
};

template < typename T > std::mutex singleton< T >::m_mutex;
template < typename T > T* singleton<T>::m_pInstance = nullptr;

class manage final : public singleton< manage > {
public:
	manage() {};
	~manage() {};
	std::string& GetName() {
		return name;
	}

private:
	unsigned int id;
	std::string name;
};

#include <stdio.h>
#include <iostream>
#include <string>
#include "game.h"

int main(int argc, char** argv)
{
	std::string mngName = manage::GetInstance()->GetName();
	mngName = "Erichao";


	std::string nowName = manage::GetInstance()->GetName();
	std::cout << mngName << std::endl;

    /* 面向过程方式进行内存释放 */
    // manage* p = manage:GetInstance();
    // delete p;

	system("pause");
	return 0;
}

2.2 单例模式析构函数

C++开发过程中,如果为给定析构函数,编译器会自动给我们提供一个析构函数。对于单例模式析构函数没有办法在继承子类中的动态函数对new的内存区域进行释放,主要是因为这种释放方式导致segment fault。产生原因是动态成员函数是依赖对象存在的,如果这个对象在本函数内部被释放,那么这个函数的this基础就不在了,导致访问内存的不确定性,系统崩溃。对于单例模式,释放方法目前可以有三种:

(1)在程序不需要这个类存在的时候通过GetInstance获取类地址,使用delete进行释放。这种方式的弊端是new和delete完全是分离开来,违背了面向对象的基本原理。

(2)类似于GetInstance方法,定义一个静态类方法,这个方法完成delete分配的内存空间。

(3)通过私有内嵌类的方法进行内存资源的释放。

对于第三种释放方法,在单例类Singleton中定义为Garbo的私有内嵌类,以防该类被在其他地方滥用。程序运行结束时,系统会调用Singleton的静态成员Garbo的析构函数,该析构函数会***单例的唯一实例使用这种方法释放单例对象的特征:
1、在单例类内部定义专有的嵌套类;
2、在单例类内定义私有的专门用于释放的静态成员;
3、利用程序在结束时析构全局变量的特性,选择最终的释放时机;
4、使用单例的代码不需要任何操作,不必关心对象的释放。
当然我们也可以直接使用智能指针的方式进行单例指针的释放,这种方式通过语言自己的特性来保证内存的恰当回收。

2.3. 多线程真正安全

Singleton模式中的实力构造器可以设置为protected以允许子类派生。

Singleton模式一般不要支持拷贝构造函数和clone接口,因为这有可能导致多个对象实例,与singleton模式的初衷违背。

如何实现多线程环境下安全的singleton?主义对双检查锁的正确实现。

//C++ 11版本之后的跨平台实现 (volatile)
std::atomic<Singleton*> Singleton::m_instance;
std::mutex Singleton::m_mutex;

Singleton* Singleton::getInstance() {
    Singleton* tmp = m_instance.load(std::memory_order_relaxed);
    std::atomic_thread_fence(std::memory_order_acquire);//获取内存fence
    if (tmp == nullptr) {
        std::lock_guard<std::mutex> lock(m_mutex);
        tmp = m_instance.load(std::memory_order_relaxed);
        if (tmp == nullptr) {
            tmp = new Singleton;
            std::atomic_thread_fence(std::memory_order_release);//释放内存fence
            m_instance.store(tmp, std::memory_order_relaxed);
        }
    }
    return tmp;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值