当前位置: 首页 > news >正文

特殊类设计

目录

前言

设计一个类,不能被拷贝

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

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

设计一个类,不能被继承

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

单例模式

(1)饿汉模式

(2)懒汉模式

总结


前言

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


设计一个类,不能被拷贝

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

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;  //定义一个静态变量,程序结束后会自动调用它的析构函数从而释放单例对象
};

总结

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

相关文章:

  • 【STL***deque容器二】
  • 多测师肖sir_高级讲师_第2个月第8讲解类
  • 各编程语言 + aardio 相互调用示例
  • SpringCloud概述
  • element的Form表单就应该这样用
  • Linux基础组件之死锁检测
  • TypeScript——函数(函数定义类型、可选参数和默认参数、剩余参数、函数类型变量、使用接口封装函数变量类型)
  • T1056点和正方形的关系 (信息学一本通C++)
  • 【Selenium】一网打尽 小窗口滑动 全窗口滑动
  • 大数据必学Java基础(六十三):COW并发容器讲解
  • AutoCAD2014与致命的错误与独显直连
  • 联邦学习系列---读书个人总结
  • MyBatis概述、maven构建、Mapper接口及ORM思想
  • 【MyCat2】学习笔记(二)
  • 【洛谷题解/USACO题解】P2746 【USACO5.3】校园网Network of Schools
  • 【comparator, comparable】小总结
  • 0x05 Python数据分析,Anaconda八斩刀
  • express如何解决request entity too large问题
  • HTTP中的ETag在移动客户端的应用
  • input的行数自动增减
  • Javascript 原型链
  • JavaScript工作原理(五):深入了解WebSockets,HTTP/2和SSE,以及如何选择
  • MD5加密原理解析及OC版原理实现
  • mockjs让前端开发独立于后端
  • Mysql优化
  • OSS Web直传 (文件图片)
  • SQL 难点解决:记录的引用
  • SQLServer之创建显式事务
  • zookeeper系列(七)实战分布式命名服务
  • 当SetTimeout遇到了字符串
  • 仿天猫超市收藏抛物线动画工具库
  • 浮动相关
  • 基于Android乐音识别(2)
  • 解决jsp引用其他项目时出现的 cannot be resolved to a type错误
  • 可能是历史上最全的CC0版权可以免费商用的图片网站
  • 每个JavaScript开发人员应阅读的书【1】 - JavaScript: The Good Parts
  • 推荐一款sublime text 3 支持JSX和es201x 代码格式化的插件
  • 1.Ext JS 建立web开发工程
  • Spring第一个helloWorld
  • ​LeetCode解法汇总2670. 找出不同元素数目差数组
  • $forceUpdate()函数
  • (02)Cartographer源码无死角解析-(03) 新数据运行与地图保存、加载地图启动仅定位模式
  • (附源码)springboot宠物医疗服务网站 毕业设计688413
  • (附源码)ssm户外用品商城 毕业设计 112346
  • (更新)A股上市公司华证ESG评级得分稳健性校验ESG得分年均值中位数(2009-2023年.12)
  • (算法二)滑动窗口
  • (一)spring cloud微服务分布式云架构 - Spring Cloud简介
  • (转)3D模板阴影原理
  • (转)setTimeout 和 setInterval 的区别
  • ***监测系统的构建(chkrootkit )
  • .NET Micro Framework初体验
  • .net 按比例显示图片的缩略图
  • :“Failed to access IIS metabase”解决方法
  • @CacheInvalidate(name = “xxx“, key = “#results.![a+b]“,multi = true)是什么意思
  • @Controller和@RestController的区别?