特殊类的设计与单例模式

1、特殊类的设计

        如何设计出一个创建出的对象只能在堆上的类?将类的默认构造函数设置为私有,再将类的拷贝构造函数设置为delete,设置静态函数GetObj,内部调用new HeapOnly,这样就只能在堆上开辟空间。

class HeapOnly
{
public:
	static HeapOnly* GetObj()
	{
		return new HeapOnly;
	}
private:
	HeapOnly()
	{}
public:
	HeapOnly(const HeapOnly&) = delete;
};

        下面照猫画虎设计一个只能在栈上创建对象的类。创建对象也是必须通过static成员函数来完成,内部调用默认构造函数。

class StackOnly
{
public:
	static StackOnly GetObj()
	{
		return StackOnly();
	}
private:
	StackOnly()
	{}
};

        还有一种思路,直接禁止了类的new函数,这样不能在堆上开辟空间了,但是这种做法是有缺陷的,他不通过静态函数来构造对象,无法阻止在数据段(静态区)创建对象,比如"static StackOnly p;"就在静态区创建了一个对象,不满足只能在栈创建对象的要求。

class StackOnly
{
public:
	void* operator new(size_t size) = delete;
};

2、单例模式

        设计模式是一套被人反复使用、多数人知晓的、经过分类的代码设计经验的总结。使用设计模式的目的是为了提高代码的可重用性,让代码更容易被他人理解,保证代码的可靠性。设计模式使得代码编写真正地工程化。

        之前已经学过的一些设计模式有(1)迭代器模式,基于面向对象三大特性之一的封装设计出来的,用一个迭代器封装以后,可以在不暴露容器的结构的情况下,以一种统一的方式访问修改容器中的数据。(2)适配器模式,体现的是一种复用的思想。

        这里介绍一下单例模式。设计一个类,在全局中(进程中)只能实例化出一个对象,将这种设计模式称为单例模式。主要应用场景是多线程共享的内存池或者用于处理任务的线程池。

        下面是一个简化的单例模式示例,为了支持单例的基本属性,要将构造函数设置为私有,将拷贝构造函数设置为delete,要通过静态成员函数GetInstance()来获取对象,如果不设置为静态成员函数,由于外部无法构造,而普通成员函数需要依靠对象来调用,因此无法获取对象。

class Singleton
{
public:
	static Singleton* GetInstance()
	{
		if (_pinst == nullptr)
		{
			_pinst = new Singleton;
		}
		return _pinst;
	}
	Singleton(const Singleton& s) = delete;
private:
	Singleton()
	{}
	static Singleton* _pinst;
};
Singleton* Singleton::_pinst = nullptr;

        上面这段代码依旧有缺陷,就是他不能有效应对线程安全问题,在上面这个类的单例还没有构造出来的时候,这时候_pinst为nullptr,假如有两个线程同时进入了GetInstance()的if判断语句里面,那么会构造出两个对象,因此需要用互斥量保护线程安全。下面是一个双检查写法,在解决线程安全问题的同时有效保证代码效率,只有在_pinst为空时会加锁解锁。还可以使用智能指针来管理_mtx,如果在加锁解锁之间出现异常,可以避免死锁。

class Singleton
{
public:
	static Singleton* GetInstance()
	{
		if (_pinst == nullptr)
		{
			_mtx.lock();
			if (_pinst == nullptr)
			{
				_pinst = new Singleton;
			}
			_mtx.unlock();
		}
		return _pinst;
	}
	Singleton(const Singleton& s) = delete;
private:
	Singleton()
	{}
	static Singleton* _pinst;
	static mutex _mtx;
};

Singleton* Singleton::_pinst = nullptr;
mutex Singleton::_mtx;

        单例模式又分为懒汉模式和饿汉模式,懒汉模式指的是在获取对象的时候再创建对象,上面的写法就是典型的懒汉模式。饿汉模式指的是在一开始(main函数之前)就创建对象,由于对象是在main函数之前创建的,创建时只有主线程,所以不存在线程安全问题,下面是一种饿汉模式的写法。

class Singleton
{
public:
	static Singleton* GetInstance()
	{
		return &_inst;
	}
private:
	static Singleton _inst;
};
Singleton Singleton::_inst;

        总结一下饿汉与懒汉的区别(1)懒汉模式需要考虑线程安全和资源释放的问题,实现相对更复杂,恶寒模式不存在以上问题,实现简单。(2)懒汉是一种懒加载毛hi,在需要时再初始化创建对象,不会影响程序的启动。饿汉模式则相反,在程序启动阶段就创建初始化实例对象,会导致程序启动慢,影响体验。(3)如果有多个单例类,假设它们之间有依赖关系,要求A单例先创建初始化,B单例再创建初始化,此处就不能使用饿汉,因为无法保证创建对象的顺序(静态对象是无法保证创建顺序的),这里要用懒汉模式,便于程序员手动控制。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值