特殊类的设计

目录

一、设计一个类,不能被拷贝

二、设计一个类,只能在堆上创建

三、设计一个类,只能在栈上创建

四、设计一个类,不能被继承

 五、单例模式

        1、饿汉模式

        2、懒汉模式

饿汉模式和懒汉模式对比  


一、设计一个类,不能被拷贝

        要设计一个不能被拷贝的类有两种方式:

        1、将拷贝构造和赋值重载设为私有

        2、用 delete 关键字,将拷贝构造和赋值重装设为删除状态

// 1、用 delete
class NoCopy1
{
public:
	NoCopy1()
	{}
	
	NoCopy1(const NoCopy1& nc) = delete;
	NoCopy1& operator=(const NoCopy1& nc) = delete;
	int a = 2;
};

// 2、将拷贝构造和赋值重载设为私有
class NoCopy2
{
public:
	NoCopy2()
	{}

private:
	NoCopy2(const NoCopy2& nc);
	NoCopy2& operator=(const NoCopy2& nc);
	int a = 2;
};

二、设计一个类,只能在堆上创建

        设计一个只能在堆上创建的类同样有两种方法:

        1、将析构函数设为私有

        将析构函数设为私有后,因为析构私有,在栈上无法对象。而用 new 在堆上创建对象是调用 delete 释放空间,而 delete 也是在类内,可以调用析构。由此实现了只能在堆上创建的类。

        2、将构造私有,对外提供一个接口,外部只能通过调用这个接口才能创建对象,而这个接口我们可以自己实现在堆上创建。由此,可以实现只能在堆上创建的类。(通用方法)

        注意:使用第二种方法时,我们需要将拷贝构造和赋值重载一并私有,因为外部可以用 HeapOnly2* ho2(*ho1); 在栈上创建。(ho1 是用 create 拿到的指针)

// 方法1,将析构函数设为私有
class HeapOnly1
{
public:
	
private:
	// 方法:析构函数设为私有
	~HeapOnly1()
	{}

};
// 方法2,将构造函数设为私有,给出 create 接口,用 new 创建并返回对象 -- 通用方法,封构造
// 注意:要把拷贝构造和赋值一并封住,防止拷贝
class HeapOnly2
{
public:
	static HeapOnly2* create()
	{
		HeapOnly2* ho = new HeapOnly2;
		return ho;
	}
private:
	HeapOnly2()
	{}
	HeapOnly2(const HeapOnly2& ho) = delete;

};

三、设计一个类,只能在栈上创建

        要设计一个只能在栈上创建的类,我们可以用上面的通用方法,将构造函数私有化,对外提供一个接口,外部只能通过这个接口拿到对象。

        这里不需要将拷贝构造和赋值重载私有,因为它们也是在栈上创建对象

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

四、设计一个类,不能被继承

        设计一个不能被继承的类通用有两种方式:

        1、C++98:将构造函数私有,它就无法被继承

        2、C++11:用 final 关键字

// 方法1,C++98方式,将构造私有
class NoInherit
{
private:
	NoInherit()
	{}
};

// 方法2,final 关键字,表示类不能被继承
class NoInherit final
{};

 五、单例模式

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

        实现方法:

        1、饿汉模式

        (假设该类的名字为 HungrySingleton)

        ①、将构造、拷贝构造、赋值重载封住

        ②、定义一个静态成员 static HungrySingleton* _instance;

        ③、对外提供一个接口,外部只能调用这个接口获得实例,返回的实例就是 _instance

        叫饿汉模式的原因,就是静态对象是在 main 函数之前创建。

// 饿汉模式:一开始就创建对象(静态成员在 main 函数之前就创建)
class HungrySingleton
{
public:
	static HungrySingleton* GetInstance()
	{
		return _instance;
	}
private:
	HungrySingleton()
	{}
	HungrySingleton(const HungrySingleton& hst) = delete;
	HungrySingleton& operator=(const HungrySingleton& hst) = delete;
private:
	static HungrySingleton* _instance;

	int _n = 1;
	vector<int> _v;
};
HungrySingleton* HungrySingleton::_instance = new HungrySingleton;

        2、懒汉模式

        (假设该类的名字为 LazySingleton)

        ①、将构造、拷贝构造、赋值重载封住

        ②、定义一个静态成员 static LazySingleton* _instance;

        ③、与饿汉模式不同,懒汉模式是需要时,才创建。也就是说:当多个线程想实例化对象时,只有第一个线程创建,其他线程都是拿第一个线程创建好的。

class LazySingleton
{
public:
	static LazySingleton* GetInstance()
	{
		// 双检查加锁,创建实例后来的线程不用竞争锁,提高效率
		if (nullptr == _lazy_instance)
		{
            // 可能有多个线程同时进来,加锁保护
			unique_lock<mutex> mtx;
			if (nullptr == _lazy_instance)
			{
                // 第一个进来的线程创建实例
				cout << "创建对象\n";
				_lazy_instance = new LazySingleton;
				return _lazy_instance;
			}
		}
        // 后面的线程就获得
		cout << "获得对象\n";
		return _lazy_instance;
	}
private:
	LazySingleton()
	{}
	LazySingleton(const LazySingleton& hst) = delete;
	LazySingleton& operator=(const LazySingleton& hst) = delete;
private:
	static LazySingleton* _lazy_instance;
	static mutex _mtx;// 加锁保护

	int _n = 1;
	vector<int> _v;
};
LazySingleton* LazySingleton::_lazy_instance = nullptr;
mutex LazySingleton::_mtx;

        ④、释放单例:一般单例不需要释放,因为全局都要使用。但有些特殊情况,如果要实现释放单例,我们也可以对外提供接口,防止多个线程同时释放,同样需要加锁保护。

        同时,在这个情况下,当程序运行结束时,单例也需要释放,因此,我们不仅要提供一个释放单例的接口,还要想办法让程序结束时能够自动调用它。

        解决方法:在 LazySingleton 类中写一个内部类 GC,GC 的析构函数就调用释放单例接口,并定义一个静态成员 static GC* _gc; 当程序运行结束,就会自动调用释放接口了。

class LazySingleton
{
public:
	static LazySingleton* GetInstance()
	{
		// 双检查加锁
		if (nullptr == _lazy_instance)
		{
			unique_lock<mutex> mtx;
			if (nullptr == _lazy_instance)
			{
				cout << "创建对象\n";
				_lazy_instance = new LazySingleton;
				return _lazy_instance;
			}
		}
		cout << "获得对象\n";
		return _lazy_instance;
	}

	static void DelInstance()
	{
		// 一般全局都有使用单例,一般不用释放单例
		// 只有特殊场景才需要
		unique_lock<mutex> lock(_mtx);
		if (_lazy_instance)
		{
			delete _lazy_instance;
			_lazy_instance = nullptr;
		}
	}

	~LazySingleton()
	{
		// 持久化,将数据写到文件
	}

	// 结束后自动调用 DelInstance
	class GC
	{
	public:
		~GC()
		{
			cout << "释放单例\n";
			DelInstance();
		}
	};
private:
	LazySingleton()
	{}
	LazySingleton(const LazySingleton& hst) = delete;
	LazySingleton& operator=(const LazySingleton& hst) = delete;
private:
	static LazySingleton* _lazy_instance;
	static mutex _mtx;
	static GC _gc;

	int _n = 1;
	vector<int> _v;
};
LazySingleton* LazySingleton::_lazy_instance = nullptr;
mutex LazySingleton::_mtx;
LazySingleton::GC LazySingleton::_gc;

饿汉模式和懒汉模式对比  

        饿汉缺点
        1、如果单例对象很大,main 函数之前申请,a、占用资源;b、影响程序启动速度
        2、如果两个单例有依赖关系,要求单例1先创建,单例2再创建,饿汉无法控制顺序
        饿汉优点:简单

        懒汉解决了饿汉的问题(推荐懒汉)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值