C++程序员修炼手册--设计模式--单例模式--懒汉模式--饿汉模式

C++程序员修炼手册--设计模式--单例模式--懒汉模式--饿汉模式,第1张

目录

一,只在堆上创建对象的类

1,实现方式

二,只在栈上创建对象的类

三,不能被继承的类

四,只能创建一个对象的类(class="superseo">单例模式)

4.1 饿汉模式

4.2,懒汉模式

五,懒汉模式与饿汉模式对比


一,只在堆上创建对象的类

概述:堆上开辟的空间是使用malloc new创建的空间,所以我们设计的类只能用new或者malloc开辟空间。

1,实现方式

将构造函数私有化,拷贝构造私有,赋值也设计成私有,避免在栈上开辟空间,使用静态成员函数调用构造函数,完成对象的创建。

class HeapOnly
{
public:
	static HeapOnly*CreatInstance()
	{
		return new HeapOnly();
	}
	//将构造函数私有化,创建静态对象构造对象 
private:
	HeapOnly()
	{}	
	HeapOnly(const HeapOnly&)=delete;
	HeapOnly& operator =(const HeapOnly&)=delete;	
}; 
二,只在栈上创建对象的类

概述:栈由 *** 作系统自动分配释放 ,用于存放函数的参数,所以不能使用new和malloc等开辟空间的做法,通过使用系统默认的变量来存放数据。

1,实现方式:构造函数放在私有成员里面,通过静态成员函数创建返回对象即可

 // 构造放私有,不适用new和malloc 
class StackOnly
{
public:
	static StackOnly CreatObj()
	{
		return StackOnly();
	}
private:
	StackOnly() 
	{}
	//C++98,只声明,不实现 
	StackOnly(const StackOnly&); 
	StackOnly& operator=(const StackOnly&);
}
三,不能被继承的类

C++98中通过将构造函数放成私有的函数,就可以实现子类无法对父类进行构造,从而无法继承

class  NonInherit 
{
public: 
	static NonInherit Getinnstance()
	{
		return NonInherit();
	}
private:
	NonInherit()
	:_a(0)
	{}	
	int _a;
}; 
class Inherit:public NonInherit//不可继承
{
};

C++11中提供了关键字final来实现类不可继承

class A final
{	
};
class B :public A
{
};
四,只能创建一个对象的类(单例模式)

        一个类只能创建一个对象,即单例模式,该模式可以保证在系统中只有一个此类对象,并提供一个全局的访问点,该实例被所有模块共享

4.1 饿汉模式

在未进main函数之前,对对象进行实例化,并提供一个全局的访问点,

缺点:如果这个类要实例化的对象很多,则在程序启动的时候比较慢,迟迟不能进入main函数。

优点:在访问这个类之前已经初始化,访问时直接返回,避免了线程同时访问时,创建出两个对象,产生内存泄漏。

实现:构造函数私有定义,提供静态全局访问函数接口,返回静态对象指向的成员指针。

class HungryMan
{
public:
	static HungryMan* GetInstance()
	{
		return _inst;
	} 
		void Print()
		{
			cout << "Print()" << _a << endl;
		}
	
private:
	HungryMan()
	:_a(0) 
	{}
	//禁用拷贝构造和赋值 
	HungryMan(const HungryMan&)=delete;
	HungryMan& operator=(const HungryMan&)=delete;
	int _a;
	//单例对象使用static,只创建一个 
	static HungryMan* _inst;
};
HungryMan* HungryMan::_inst=new HungryMan;
4.2,懒汉模式

        如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好。

 优点:第一次使用实例对象时,创建对象。进程启动无负载。多个单例实例启动顺序自由控制。
 缺点:复杂

实现:此时为了调用对象时,初始化只能一次,为了防止线程同时调用,需要加互斥锁,在初始化时,加锁,初始化完成,解锁,从此以后,直接返回对象指针。

class Lazy
{
	//如果多线程同时访问这个对象,可能存在都为空,然后同时创建,形成内存泄漏
	//所以此时需要加锁 
public: 
	static Lazy* GetInstance()
	{
		// 保护第一次需要加锁,后面都不需要加锁的场景,可以使用双检查加锁
		// 特点:第一次加锁,后面不加锁,保护线程安全,同时提高了效率
		if(_inst==nullptr)
		{ 
			_mtx.lock();//加锁 
			if(_inst==nullptr)
			{
				_inst=new Lazy;
			}
			_mtx.unlock();//解锁 
		} 
		return _inst;
	}
	void print()
	{
		cout<<_inst<<"   "<<_a<
五,懒汉模式与饿汉模式对比

  懒汉模式和饿汉模式的对比

  饿汉
  优点:简单 
  缺点:1、如果单例对象构造函数工作比较多,会导致程序启动慢,迟迟进不了入口main函数
             2、如果有多个单例对象,他们之间有初始化依赖关系,饿汉模式也会有问题。 
        比如有A和B两个单例类,要求A单例先初始化,B必须在A之后初始化。那么饿汉无法保证
        这种场景下面用懒汉就可以,懒汉可以先调用A::GetInstance(),再调用B::GetInstance().
  懒汉
  优点:解决上面饿汉的缺点。因为他是第一次调用GetInstance时创建初始化单例对象
  缺点:相对饿汉,复杂一点点。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1498770.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-25
下一篇 2022-06-25

发表评论

登录后才能评论

评论列表(0条)