• 【C++】特殊类的设计(只在堆、栈创建对象,单例对象)


    🌏博客主页: 主页
    🔖系列专栏: C++
    ❤️感谢大家点赞👍收藏⭐评论✍️
    😍期待与大家一起进步!



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

    实现方式:

    1. 将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。
    2. 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建
    class HeapOnly
    {
    public:
    	static HeapOnly* CreateObj()
    	{
    		return new HeapOnly;
    	}
    private:
    	HeapOnly()
    	{
    		//...
    	}
    
    	HeapOnly(const HeapOnly& hp) = delete;
    	HeapOnly& operator=(const HeapOnly& hp) = delete;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

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

    实现方法:
    1.同上将构造函数私有化,然后设计静态方法创建对象返回即可
    2.禁用堆上的创建方式,new=operator new+构造函数
    3.operator new 是系统提供的全局函数,new在底层调用operator new全局函数来申请空间

    class StackOnly
    {
    public:
    	static StackOnly CreateObj()
    	{
    		return StackOnly();
    	}
    
    	// 禁掉operator new可以把下面用new 调用拷贝构造申请对象给禁掉
     // StackOnly obj = StackOnly::CreateObj();
     // StackOnly* ptr3 = new StackOnly(obj);
    	//  new==operator new + 构造
    	//delete==析构+operator delete
    	void* operator new(size_t size) = delete;
    	void operator delete(void* p) = delete;
    private:
    	//构造函数私有化
    	//拷贝构造函数不能私有化,因为我们上面CreateObj()返回,需要
    	//用到拷贝构造函数
    	StackOnly()
    		:_a(0)
    	{}
    private:
    	int _a;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

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

    1.方法一:

    //  构造函数私有化,派生类中调不到基类的构造函数。则无法继承
    class NonInherit
    {
    public:
     static NonInherit GetInstance()
     {
     return NonInherit();
     }
    private:
     NonInherit()
     {}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.方法二:

    //final关键字,final修饰类,表示该类不能被继承。
    class A  final
    {
        // ....
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

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

    单例模式:
    一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。 比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理

    1.饿汉模式

    就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。

    class Singleton
    {
    public:
    	// 2、提供获取单例对象的接口函数
    	static Singleton& GetInstance()
    	{
    		return _sinst;
    	}
    
    private:
    	// 1、构造函数私有
    	//构造函数私有也不能调用new了
    	Singleton()
    	{
    		// ...
    	}
    
    	// 3、防拷贝
    	Singleton(const Singleton& s) = delete;
    	Singleton& operator=(const Singleton& s) = delete;
    
    	static Singleton _sinst;
    	//虽然为静态类型,但依然可以使用对应类里面的构造函数
    };
    Singleton Singleton::_sinst;// 在程序入口之前就完成单例对象的初始化
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    饿汉模式:一开始(main函数之前)就创建单例对象
    1、如果单例对象初始化内容很多,影响启动速度
    2、如果两个单例类,互相有依赖关系。
    假设有A B两个单例类,要求A先创建,B再创建,B的初始化创建依赖A,但在main的外面没办法确定哪个类先被创建,可能会出现问题

    2.懒汉模式

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

    1.普通场景

    class Singleton
    	{
    	public:
    		// 2、提供获取单例对象的接口函数
    		static Singleton& GetInstance()
    		{
    			if (_psinst == nullptr)
    			{
    				// 第一次调用GetInstance的时候创建单例对象
    				_psinst = new Singleton;
    			}
    
    			return *_psinst;
    		}
    
    	private:
    		// 1、构造函数私有
    		Singleton()
    		{
    			// ...
    		}
    
    		~Singleton()
    		{
    		 
    		}
    
    		// 3、防拷贝
    		Singleton(const Singleton& s) = delete;
    		Singleton& operator=(const Singleton& s) = delete;
    
    	 
    
    		static Singleton* _psinst;
    	 
    	};
    	Singleton* Singleton::_psinst = nullptr;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    2.特殊场景

    1、中途需要显示释放
    static void DelInstance()
    		{
    			if (_psinst)
    			{
    				delete _psinst;
    				_psinst = nullptr;
    			}
    			//自己写一个静态的函数,进行释放操作
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    2.程序结束时,需要做一些特殊动作(如持久化)

    方法:
    1.我们把要写入的数据过程放到析构函数中
    2.我们可以像智能指针那样使用,在类里面再定义一个类GC,让GC的析构函数专门用来管理Singleton的析构函数,然后创建GC的对象,因为GC不是指针类型为普通类型,程序结束的时候会自动调用其析构函数,这样也就完成了Singleton的析构函数

    class GC
    		{
    		public:
    			~GC()
    			{
    				 Singleton::DelInstance();
    			}
    		};
    		 
    	~Singleton()
    		{
    			cout << "~Singleton()" << endl;
    
    			// map数据写到文件中
    			FILE* fin = fopen("map.txt", "w");
    			for (auto& e : _dict)
    			{
    				fputs(e.first.c_str(), fin);
    				fputs(":", fin);
    				fputs(e.second.c_str(), fin);
    				fputs("\n", fin);
    			}
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述

    3.源码
    class Singleton
    	{
    	public:
    		// 2、提供获取单例对象的接口函数
    		static Singleton& GetInstance()
    		{
    			if (_psinst == nullptr)
    			{
    				// 第一次调用GetInstance的时候创建单例对象
    				_psinst = new Singleton;
    			}
    
    			return *_psinst;
    		}
    
    		// 一般单例不用释放。
    		// 特殊场景:1、中途需要显示释放  2、程序结束时,需要做一些特殊动作(如持久化)
    		static void DelInstance()
    		{
    			if (_psinst)
    			{
    				delete _psinst;
    				_psinst = nullptr;
    			}
    		}
    
    		 
    		class GC
    		{
    		public:
    			~GC()
    			{
    				 Singleton::DelInstance();
    			}
    		};
    
    	private:
    		// 1、构造函数私有
    		Singleton()
    		{
    			// ...
    		}
    
    		~Singleton()
    		{
    			cout << "~Singleton()" << endl;
    
    			// map数据写到文件中
    			FILE* fin = fopen("map.txt", "w");
    			for (auto& e : _dict)
    			{
    				fputs(e.first.c_str(), fin);
    				fputs(":", fin);
    				fputs(e.second.c_str(), fin);
    				fputs("\n", fin);
    			}
    		}
    
    		// 3、防拷贝
    		Singleton(const Singleton& s) = delete;
    		Singleton& operator=(const Singleton& s) = delete;
    
    		map<string, string> _dict;
    		// ...
    
    		static Singleton* _psinst;
    		static GC _gc;
    	};
    
    	Singleton* Singleton::_psinst = nullptr;
    	Singleton::GC Singleton::_gc;
     
     
    
    
    	class A {
    	public:
    		A() {
    			cout << "gouzao" << endl;
    		}
    		~A() {
    			cout << "析构函数" << endl;
    		}
    	};
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
  • 相关阅读:
    ARM DIY(九)陀螺仪调试
    css:详解BFC块级格式化上下文
    Git命令图
    通关GO语言14 内存分配:new 还是 make?什么情况下该用谁?
    【3568开发板教程上新】Android11移植开发教程升级来袭!
    MySQL基础学习总结(二)
    Redis-概念、安装、基本配置
    ..\FreeRTOS\include\FreeRTOS.h(1277): error: #65: expected a “;“出现的这个错误标志解决方法
    微服务SpringBoot 整合Redis 实现点赞、点赞排行榜
    C++-Cmake指令:set
  • 原文地址:https://blog.csdn.net/m0_74774759/article/details/133935278