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

C++知识点 -- 特殊类设计

C++知识点 – 特殊类设计

文章目录

  • C++知识点 -- 特殊类设计
  • 一、不能被拷贝的类
  • 二、 只能在堆上创建对象的类
  • 三、 只能在栈上创建对象的类
  • 四、 不能被继承的类
  • 五、 只能创建一个对象的类(单例模式)
    • 1.饿汉模式
    • 2.懒汉模式
    • 3. 单例对象释放问题:


一、不能被拷贝的类

不让编译器默认生成拷贝构造和赋值重载,c++11可以在默认成员函数后加上 = delete,表示让编译器删除该默认成员函数;

class CopyBan
{
public:
	CopyBan(const CopyBan& cb) = delete;
	CopyBan& operator=(const CopyBan& cb) = delete;

private:

	int _a;
};

二、 只能在堆上创建对象的类

在这里插入图片描述
对于普通类,在栈、堆和静态区都能够创建对象;
要设计只能在堆上创建对象的类,就需要堆构造函数或者析构函数做出一定调整;

方法一
析构函数设为私有,这样在栈和静态区的对象就无法调用析构函数销毁对象,只有在对上创建的对象调用delete才能够销毁对象,可以写一个公有函数来delete对象,完成对象的销毁;

class HeapOnly
{
public:
	//公有函数调用delete销毁对象,设置成静态成员,使用类域就能够调用
	static void Delete(HeapOnly* p)
	{
		delete p;
	}

private:
	//析构函数私有化
	~HeapOnly()
	{}

	int _a;
};

在这里插入图片描述
可以看出,栈和静态区都无法创建StackOnly对象,只有通过new才能在堆上创建对象;

方法二
构造函数私有化,这样在哪里都无法创建对象了,再添加一个公有的静态成员函数,完成函数内部在堆上创建对象,同时将拷贝构造和赋值重载也设为私有,否则还能通过拷贝的方式在其他地方创建对象;

class HeapOnly
{
public:
	//提供一个公有的,在堆上创建对象的方式
	static HeapOnly* CreatObj()
	{
		HeapOnly* pho = new HeapOnly;
		return pho;
	}

private:

	//构造函数私有化
	HeapOnly()
	{}
	//拷贝构造和赋值重载也删除
	HeapOnly(const HeapOnly& so) = delete;
	HeapOnly& operator=(const HeapOnly& so) = delete;

	int _a;
};

在这里插入图片描述
效果一样;

三、 只能在栈上创建对象的类

构造函数私有化,提供一个公有的静态成员函数,函数内部调用构造函数在堆上创建对象,返回该对象

但是这种方式并不能阻止拷贝构造在其他地方创建对象,简单地将拷贝构造和赋值重载delete,会导致CreateObj这个函数传值返回出问题,因为传值返回需要调用拷贝构造,我们可以通过delete掉operator new来限制new创建对象

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

private:
	StackOnly()
		: _a(0)
	{}

	void* operator new(size_t n) = delete;

	int _a;
};

在这里插入图片描述
这种方法并不能防止在静态区创建对象;

四、 不能被继承的类

在c++11中,用final修饰一个类,表示该类不能被继承;

//该类无法被继承
class A final
{

};

五、 只能创建一个对象的类(单例模式)

一个类只能创建一个对象,就是单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享;
单例模式有两种实现:

1.饿汉模式

程序启动时就创建一个唯一实例对象;
(1)构造函数私有化,就不能通过其他方式初始化了;
(2)私有成员中声明一个静态的指向该对象的指针,静态成员属于这个类;
(3)在类外使用new初始化这个静态指针;
(4)提供公有函数来返回这个指向对象的静态指针;

class Singleton
{
public:
	static Singleton* GetInstace()
	{
		return _pinst;
	}

	void* Alloc(size_t n)
	{
		void* ptr = nullptr;
		//...
		return ptr;
	}

	void Delloc(void* ptr)
	{
		//...
	}

private:
	//构造函数私有化
	Singleton()
	{}

	char* _ptr = nullptr;
	static Singleton* _pinst;//声明
};

//定义
Singleton* Singleton::_pinst = new Singleton;

//使用
void test()
{
	void* ptr1 = Singleton::GetInstace()->Alloc(10);
	Singleton::GetInstace()->Delloc(ptr1);

}

优点:简单、没有线程安全问题
缺点:
(1)一个程序中,多个单例,并且有先后创建初始化顺序要求时,饿汉无法控制。
比如程序两个单例类A 和 B,假设要求A先创建初始化,B再创建初始化。
(2)饿汉单例类,初始化时任务多,会影响程序启动速度。

2.懒汉模式

第一次使用对象时才创建实例对象;
(1)构造函数私有化,私有静态对象指针;
(2)公有静态成员函数,只有在对象指针为空的时候才创建对象,返回指向对象的指针;
(3)需要释放资源的场景,可以内嵌一个垃圾回收类;
实例化出一个静态垃圾回收对象,出了对象作用域(main函数结束后)就会调用析构,在析构里面将单例对象的资源全部释放;

class Singleton
{
public:
	static Singleton* GetInstance()
	{
		if (_pinst == nullptr)
		{
			_pinst = new Singleton;
		}

		return _pinst;
	}

	void* Alloc(size_t n)
	{
		void* ptr = nullptr;
		//...
		return ptr;
	}

	void Delloc(void* ptr)
	{
		//...
	}

	//内嵌垃圾回收类
	class CGarbo
	{
	public:
		~CGarbo()
		{
			if (_pinst)
			{
				delete _pinst;
			}
		}
	};

private:
	//构造函数私有化
	Singleton()
	{}

	char* _ptr = nullptr;
	static Singleton* _pinst;//声明
};

//定义
Singleton* Singleton::_pinst = nullptr;

//回收对象:main函数结束后,会调用析构函数,就会释放单例对象
static Singleton::CGarbo gc;


void test()
{
	void* ptr1 = Singleton::GetInstance()->Alloc(10);
	Singleton::GetInstance()->Delloc(ptr1);
}

优点:
(1)控制顺序
(2)不影响启动速度
缺点:
(1)相对复杂
(2)线程安全问题要处理好

3. 单例对象释放问题:

(1)一般情况下,单例对象不需要释放的。因为一般整个程序运行期间都可能会用它。
单例对象在进程正常结束后,也会资源释放。
(2)有些特殊场景需要释放,比如单例对象析构时,要进行一些持久化(往文件、数据库写)操作。


http://www.kler.cn/news/18184.html

相关文章:

  • 【Java】面试常问知识点(Java基础—2)
  • Word下划线怎么打?速速get这5个实用方法!
  • 将webrtc的音频模式改为共享模式
  • SEO优化新手必须掌握的10个技巧和工具
  • 点成分享丨细胞培养三步骤——复苏、传代、冻存
  • Java并发编程实践学习笔记(三)——共享对象之可见性
  • Dart语法下
  • 基于springboot的私人健身与教练预约管理系统
  • 散列查找实验(开散列) 题目编号:583
  • Python小姿势 - # Python相关技术知识点
  • 右值引用和移动构造函数
  • 网络机顶盒哪个牌子好?资深数码粉分享网络电视机顶盒排名
  • WRF模式
  • 如何建设智慧档案馆
  • 【Leetcode 161】【GO】相隔为 1 的编辑距离
  • @Async异步线程:Spring 自带的异步解决方案
  • 同步辐射散射数据处理:从测量到分析的全流程解析
  • Ubuntu18.04系统及相关软件安装恢复过程
  • MG100-Hi3798MV100-当贝纯净桌面卡刷固件包
  • 华为OD机试真题-24点运算【2023】【JAVA】
  • Linux安装canal
  • Go type关键字定义新类型和类型别名的区别
  • io,nio,aio区别
  • 测试开发如何进阶?需要哪些能力?吐血整理-你的进阶之路...
  • 深入理解移动端布局:Viewport与设备像素比
  • linux命令之kill详解
  • UICollectionView 实现整页翻动(每页3个cell)
  • Android 9.0 Camera2 拍照功能默认选前摄像头
  • 【论文阅读】A Comparative Study on Camera-Radar Calibration Methods
  • 如何提高执行力