特殊类的设计(含单例模式)

目录

一.不能拷贝的类

二.只能在堆上创建对象

三.只能在栈上创建对象

 四.不能被继承的类

五.只能创建一个对象(单例模式)


一.不能拷贝的类

首先要知道拷贝的场景:拷贝构造函数以及赋值运算符重载,想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。

方法1:将这两个函数只声明,不定义(防止编译器默认生成,但不能防止用户可在外面定义),或者把这两个函数设置为私有。

方法2:C++11的方式将这两个函数删除(用delete)。

例如:

class Copyban
{
public:
	Copyban()
	:_a(99)
	{
	}	
private:
	//Copyban(Copyban&);
	//Copyban& operator=(const Copyban&);

	Copyban(Copyban&) = delete;
	Copyban& operator=(const Copyban&) = delete;
	int _a;
};

二.只能在堆上创建对象

1. 将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。
2. 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建。
例如:
class HeapOnly
{
public:
	static HeapOnly* CreateObj()
	{
		return new HeapOnly;
	}
private:
	
	HeapOnly()
	{}
	HeapOnly(const HeapOnly&);
	//HeapOnly(const HeapOnly&) = delete;
};

三.只能在栈上创建对象

方法1:
将构造函数私有化,后用静态成员函数返回创建的对象。
例如:
class StackOnly
{
public:

	static StackOnly CreateObj()
	{
		return StackOnly();
	}
private:

	//StackOnly(StackOnly&) = delete;
	StackOnly()
	{}
};

但无法防止在外面拷贝构造在创建对象。

StackOnly obj1 = StackOnly::CreateObj(); 

static StackOnly obj2(obj1); //在静态区拷贝构造对象 

StackOnly* ptr = new StackOnly(obj1); //在堆上拷贝构造对象

方法2:

屏蔽operator new函数和operator delete函数,同时把构造函数私有化,用静态函数返回对象

new和delete默认调用的是全局的operator new函数和operator delete函数,但若是一个类重载了专属的operator new函数和operator delete函数,那么new和delete就会调用这个专属的函数。

例如:

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

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

但依旧无法防止在静态区拷贝构造对象。

8ac57f377bc64f52adcf30eff1f5c8df.png

 四.不能被继承的类

只要子类不能调用父类的构造函数即可。

方法1:

与前面类似,将构造函数私有化,用一个静态函数返回对象即可。

方法2:

用c++11的关键字,final,表示该类不能被继承。

例如:

class A  final
{
    // ....
};

五.只能创建一个对象(单例模式)

一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个
访问它的全局访问点,该实例被所有程序模块共享
比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象同一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。
单例模式有两种实现方式,分别是饿汉模式和懒汉模式:
饿汉模式:程序启动时就创建一个唯一的实例对象
实现方式:
  1. 将构造函数设置为私有,并将拷贝构造函数和赋值运算符重载函数设置为私有或删除,防止外部创建或拷贝对象。
  2. 提供一个指向单例对象的static指针,并在程序入口之前完成单例对象的初始化。
  3. 提供一个全局访问点获取单例对象。
例如:
class Singleton
{
public:
	//3、提供一个全局访问点获取单例对象
	static Singleton* GetInstance()
	{
		return _inst;
	}
private:
	//1、将构造函数设置为私有,并防拷贝
	Singleton()
	{}
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
	//2、提供一个指向单例对象的static指针
	static Singleton* _inst;
};
//在程序入口之前完成单例对象的初始化
Singleton* Singleton::_inst = new Singleton;

赋值重载是对于已经存在的对象,这里可以不做处理。

关于单例模式(饿汉实现)线程安全的问题:

饿汉模式是在程序运行主函数之前就完成了单例对象的创建,由于main函数之前是不存在多线程的,因此饿汉模式下单例对象的创建过程是线程安全的。后面访问这个对象是一个读的操作,也不会造成线程不安全。

懒汉模式:

  1. 将构造函数设置为私有,并将拷贝构造函数和赋值运算符重载函数设置为私有或删除,防止外部创建或拷贝对象。
  2. 提供一个指向单例对象的static指针,并在程序入口之前先将其初始化为空。
  3. 提供一个全局访问点获取单例对象。

例如:

在获取单例对象时进行加锁处理,防止多线程时创建多个对象。

class Singleton
{
public:
	//3、提供一个全局访问点获取单例对象
	static Singleton* GetInstance()
	{
		//加锁
		if (_inst == nullptr)
		{
	     	_inst = new Singleton;
		}
		//解锁
		return _inst;
	}
private:
	//1、将构造函数设置为私有,并防拷贝
	Singleton()
	{}
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
	//2、提供一个指向单例对象的static指针
	static Singleton* _inst;
}

//在程序入口之前先将static指针初始化为空
Singleton* Singleton::_inst = nullptr;
当对象被创建出来之后,只是进行读操作,没必要再加锁,改进后:当该指针不为空(对象已创建)后直接返回,没必要加锁。
static Singleton* GetInstance()
	{
		if (_inst == nullptr)
		{
		    //加锁
			if (_inst == nullptr)
			{
				_inst = new Singleton;
			}
			//解锁
		}
		return _inst;
	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值