特殊类设计

设计一个类,只能在堆上创建对象

实现方法
  1. 将构造函数私有化,只给用户提供一个获取对象的接口,接口内部是在堆上创建对象;
  2. 因为用户不能自己创建对象,所以就无法调用我们的接口来获取对象,所以这个接口必须被设计为静态接口,这样用户可以通过类名来调用接口获取对象;
  3. 因为拷贝构造出来的对象是在栈上,所以我们应该将拷贝构造函数禁用,这里有两种方法禁用拷贝构造:
    • 将拷贝构造函数声明为私有,且不实现,有个缺点:这样做不能杜绝在类内部定义拷贝构造函数;
    • 将拷贝构造函数置为delete,完全杜绝;
class HeapOnly{
public:
	static HeapOnly* CreateObject(){
		return new HeapOnly;
	}
private:
	HeapOnly() {}
	// C++98
	// 1.声明成私有,且只声明,不实现
	HeapOnly(const HeapOnly&)// C++11
	HeapOnly(const HeapOnly&) = delete;
};

设计一个类,只能在栈上创建对象

实现方法一
  1. 将构造函数私有化,只给用户提供一个获取对象的接口,接口内部是在栈上创建对象;
  2. 因为用户不能自己创建对象,所以就无法调用我们的接口来获取对象,所以这个接口必须被设计为静态接口,这样用户可以通过类名来调用接口获取对象;
class StackOnly{
public:
	static StackOnly CreateObject(){
		return StackOnly();
	}
private:
	StackOnly() {}
};
实现方法二
  • 屏蔽new的功能就可以防止用户在堆上创建对象,在前面学习new的时候说过,new在底层调用的是void* operator new(size_t size)函数,因此我们将operator new()函数屏蔽掉,这样就可以屏蔽new了,当然也要防止定位new,但其实屏蔽了operator new(),实际也将定位new屏蔽掉;
  • newdelete是用户进行动态内存申请和释放的操作符,operator newoperator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间;
  • 使用这种方法有一个 bug,那就是全局的对象可以被创建在堆上;
class StackOnly{
public:
	StackOnly() {}
private:
	void* operator new(size_t size);
	void operator delete(void* p);

	void* operator new(size_t size) = delete;
	void operator delete(void* p) = delete;
};

设计一个类,不能被拷贝

实现方法
  • 将拷贝构造函数与赋值运算符函数设置为私有,只声明不定义;或者将拷贝构造函数与赋值运算符函数置为delete
class CopyBan{
	// ...
	private:
	CopyBan(const CopyBan&);
	CopyBan& operator=(const CopyBan&);
	//...
	CopyBan(const CopyBan&)=delete;
	CopyBan& operator=(const CopyBan&)=delete;
};

设计一个类,不能被继承

实现方法
  • C++98 中,可以将一个类的构造函数私有化,派生类中调用不到基类的构造函数,则无法继承;
  • C++11 中,可以使用final关键字,表明这是一个最终类,不可被继承;
// C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承
class NonInherit{
public:
	static NonInherit GetInstance(){
		return NonInherit();
	}
private:
	NonInherit()
	{}
};
//C++11 中
class A final
{
	// ....
};

设计一个类,只能创建一个对象

概念
  • 一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享;比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理;
饿汉模式
  • 不管用不用这个类的对象,在程序启动时都会为该类实例化一个对象,如何实现呢?在类中定义一个该类的静态对象成员变量,因为静态成员在程序运行时就会确定并分配空间,且只有一个;
  • 优点:简单;
  • 缺点:因为在一开始要加载所有数据,可能会导致进程启动慢,如果有多个单例类对象实例启动,则顺序不确定;
// 饿汉模式
class Singleton{
public:
	//提供获取这个类实例化的那个唯一对象
	static Singleton* GetInstance(){
		return &m_instance;
	}
private:
	// 构造函数私有,防止多个对象实例
	Singleton(){};
	//要禁止拷贝,拷贝也会创建一个新的对象
	// C++98 防拷贝
	Singleton(Singleton const&);
	Singleton& operator=(Singleton const&);
	// C++11 防拷贝
	Singleton(Singleton const&) = delete;
	Singleton& operator=(Singleton const&) = delete;
	static Singleton m_instance;
};
Singleton Singleton::m_instance; // 在程序入口之前就完成单例对象的初始化
懒汉模式
  • 饿汉模式在一开始就将所有东西都加载了,保证了后续的效率,但是有些时候有些数据根本用不到,所以出现了懒汉模式,使用的时候再创建对象,也叫延迟加载;
  • 优点:第一次使用实例对象时,才会创建对象;进程启动时无负载,启动快;多个单例实例启动顺序自由控制;
  • 缺点:复杂;
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
class Singleton{
public:
	static Singleton* GetInstance() {
		// 注意这里一定要使用Double-Check的方式加锁,才能保证效率和线程安全
		//外面的这层检查是为了提高效率,当对象指针为空时,进程才会加锁,避免什么情况下都加锁,浪费时间
		if (nullptr == m_pInstance) {
			//加锁,是为了保证线程安全,防止多个线程拿到对象指针,实例化多个对象
			m_mtx.lock();
			//这里的检查是为了判断对象指针是否为空,为空则加载资源,否则不加载
			if (nullptr == m_pInstance) {
				m_pInstance = new Singleton();
			}
			m_mtx.unlock();
		}
		return m_pInstance;
	}
	// 实现一个内嵌垃圾回收类
	class CGarbo {
	public:
		~CGarbo(){
		if (Singleton::m_pInstance)
			//释放资源空间
			delete Singleton::m_pInstance;
		}
	};
	// 定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象
	static CGarbo Garbo;
private:
	// 构造函数私有
	Singleton(){};
	// 防拷贝
	Singleton(Singleton const&);
	Singleton& operator=(Singleton const&);
	static Singleton* m_pInstance; // 单例对象指针
	static mutex m_mtx; //互斥锁
};
//类外初始化
Singleton* Singleton::m_pInstance = nullptr;
Singleton::CGarbo Garbo;
mutex Singleton::m_mtx;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值