特殊类设计


前言

小伙伴们大家好,本文主要介绍一些常见特殊类的设计方式,希望能给大家带来帮助。


设计一个类,不能被拷贝

拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,
只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。

C++98:

实现方法:将拷贝构造函数和赋值运算符都设为私有即可满足要求。这里只需声明无需定义,因为该函数根本不会调用,定义了也没有意义。

class CopyBan
{
  // ...
 
private:
  CopyBan(const CopyBan& x);
  CopyBan& operator=(const CopyBan& x);
  //...
};

c++11: 

实现方法:c++11新增加了delete的用法,除了用来释放new申请的空间外,还可以用来禁用类中的函数,在后面加上=delete即可。

class CopyBan
{
  // ...
  CopyBan(const CopyBan& x)=delete;
  CopyBan& operator=(const CopyBan& x)=delete;
  //...
};

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

实现方法:

  • 将类的构造函数私有,为了防止居心叵测者用拷贝构造搞事,我们要把拷贝构造也声明的私有。
  • 创建一个静态成员函数,在静态成员函数里面去创建对象,而且指定在堆中创建。这里一定要用静态成员函数,不然没有对象调不出来,就变成了先有鸡还是先有蛋的问题。
class HeapOnly
{
public:
	static HeapOnly* create()
	{
		return new HeapOnly();
	}
private:
	HeapOnly()
	{
		//
	}
	HeapOnly(const HeapOnly& x);
};

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

c++ 98:

实现方法:在类中重载new和delete,并设为私有化,编译器会优先调用类中的new和delete。

class StackOnly
{
public:
	StackOnly()  {}

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

c++11:

实现方法:直接使用delete禁掉重载后的new和delete

class StackOnly
{
public:
	StackOnly()  {}

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

设计一个类,不能被继承

c++98:

实现方法:将类的构造函数私有化,派生类调不到基类的构造函数,无法创建对象。这种操作其实并不彻底,派生类在定义类时依旧可以进行继承操作,只有在创建对象时才会报错。

class NonInherit
{
public:
	static NonInherit GetInstance()
	{
		return NonInherit();
	}
private:
	NonInherit()
	{}
};

c++11: 

实现方法: final关键字,final修饰类,表示该类不能被继承。该操作会让类彻底不能继承。

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

//class C : A   会报错
//{};

设计一个类,只能创建一个对象(单例模式) 

设计模式是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。为什么会产生设计模式这样的东西呢?就像人类历史发展会产生兵法。最开始部落之间打仗时都是人拼人的对砍。后来春秋战国时期,七国之间经常打仗,就发现打仗也是有套路的,后来孙子就总结出了《孙子兵法》。孙子兵法也是类似。使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。

单例模式

一个类只能创建一个对象,这种模式叫做单例模式。该模式保证了系统中该类只有一个实例,并提供一个全局接口,该实例被所有程序模块共享。单例模式有两种实现模式。

(1)饿汉模式

饿汉模式,顾名思义,这个模式处于一个非常饥饿的状态。表现形式就是不管将来用的到用不到,程序启动时就要创建一个唯一的实例对象。

优点和缺点:

  • 优点:非常简单
  • 缺点:在类的构造函数中需要完成大量任务时会导致程序启动很慢,比如登陆qq时需要等好久,一直卡在登陆页面,让人分分钟有砸键盘的冲动。而且多个单例类一般处在不同的文件,实例化顺序不确定。

下面我们来学习饿汉模式是怎样实现的。单例模式最重要的就是要保证一个类只能创建一个对象,所以首先我们要把他的构造函数,拷贝,赋值等操作都封起来都私有化。然后设置一个静态变量指针,利用静态变量在类外初始化并且所有对象共享静态变量这一特性,我们可以利用静态变量new一个对象出来,并保存这个对象的地址。由于静态变量是私有成员,除初始化外不能在类外被访问,所以不会被更改,保证了对象的唯一性。具体代码如下:

class Singleton
{
public:
	static Singleton* get_instance()
	{
		return _inst;
	}
private:
	Singleton()
	{}
	Singleton(const Singleton& x);
	Singleton& operator=(Singleton& x);
	static Singleton* _inst;
};

Singleton* Singleton::_inst = new Singleton();//在函数入口前就完成初始化
int main()
{
	cout << Singleton::get_instance()<<endl;
	cout << Singleton::get_instance() << endl;
	cout << Singleton::get_instance() << endl;
	const int a = 10;
}

(2)懒汉模式

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

优点和缺点:

  • 优点:实例对象时进程无负载,可以快速启动,而且可以自由控制单例实例的启动顺序。
  • 缺点:复杂,需要加锁来保证安全

懒汉模式和饿汉模式的最大区别就是需要的时候才会创建单例对象,所以我们把创建单例对象的步骤写在接口内部。要注意的是,饿汉模式是在函数入口内部就已经初始化好了单例对象,并不会受到多线程的影响,而懒汉模式在函数入口进行创建对象,在创建对象过程中必须加锁保护。

在加锁时采用的是双检查模式,这种模式是非常巧妙的,如果取消最外层的检查,只保留一层检查,看上去似乎也能完成任务,但实际会大大影响效率。因为单例对象只会被创建一次,也就是说这把锁只需要被用到一次以后就没有任何意义了,在只有一层检查的情况下,_inst指针不为空后进程跑到这里依旧会去申请锁,没有申请到锁的进程就需要挂起等待,严重影响了效率。所以需要在外面再加一层检查,如果指针已经不为空了进程根本不会再去申请锁。

class Singleton
{
public:
	static Singleton* GetInstance()
	{
		// 保护第一次需要加锁,后面都不需要加锁的场景,可以使用双检查加锁
		// 特点:第一次加锁,后面不加锁,保护线程安全,同时提高了效率
		if (_inst == nullptr)
		{
			_mtx.lock();
			if (_inst == nullptr)
			{
				_inst = new Singleton;
			}
			_mtx.unlock();
		}

		return _inst;
	}
private:
	Singleton()
	{
		// 假设单例类构造函数中,要做很多配置初始化
	}
	~Singleton()
	{
		// 程序结束时,需要处理一下,持久化保存一些数据
	}
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
	// 实现一个内嵌垃圾回收类    
	class CGarbo {
	public:
		~CGarbo()
		{
			if (_inst)
			{
				delete _inst;
				_inst = nullptr;
			}
		}
	};
	static Singleton* _inst;
	static std::mutex _mtx;
	static CGarbo _gc;  //定义一个静态变量,程序结束后会自动调用它的析构函数从而释放单例对象
};

总结

今天的内容就到这里了,本文主要简单介绍了一些特殊类的设计方法,希望能给大家带来帮助。江湖路远,来日方长,我们下次见。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值