• 【C++进阶之路】特殊类的设计


    1.只能创建在堆上的对象

    1.1析构函数私有

    • 需要我们手动创建接口进行释放
    class HeapOnly
    {
    public:
    	void Destory()
    	{
    		delete this;//this指向的就是需要释放的资源
    		//this = nullptr; 
    		//说明:this 指针是 T * const this 类型的 即this指针不可被修改!因此无法置空
    	}
    private:
    	~HeapOnly()
    	{
    		//...
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    1.2构造函数私有

    class HeapOnly
    {
    
    public:
    	//static 公有成员函数可以直接通过类访问限定符进行访问。
    	static HeapOnly* CreatHeapOnly()
    	{
    		return new HeapOnly;
    	}
    	//其实不加static也可以调用,想一下如何实现。
    	
    private:
    	//防止new出来的对象再进行拷贝构造局部对象
    	HeapOnly(const HeapOnly& v) = delete;
    	//赋值要不要封呢?可以封,不过没有必要,因为赋值是已存在的两个对象进行的赋值操作
    	//我们封的是对已存在的对象进行初始化的操作。
    	//防止生成局部对象
    	HeapOnly()
    	{
    		//...
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    2.只能创建在栈上的对象

    • 博主觉得准确来说,应该是无法在堆上创建的对象。
    class NoneHeap
    {
    public:
    	static NoneHeap CreatHeapObj()
    	{
    		NoneHeap st;
    		return st;//此处类外需要调用拷贝构造,因此无法对拷贝构造进行私有。
    	}
    
    private:
    	//把构造私有化
    	NoneHeap()
    	{
    		//...
    	}
    	void* operator new(size_t);
    	//通过对new的理解——调用operator new + 构造函数。
    	//这里的拷贝构造是无法进行实现的屏蔽的,这里的拷贝构造创建对象需要使用.
    	//因此无法进行屏蔽,所以只能通过封闭new来封闭堆空间的开辟。
    	//那既然这样对构造函数的封闭就显得无关紧要了。
    	void operator delete(void*);
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 简化一下:
    class NoneHeap
    {
    public:
    	//把构造私有化
    	NoneHeap()
    	{
    		//...
    	}
    private:
    	void* operator new(size_t);
    	void operator delete(void*);
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    缺陷:无法禁用static对象和全局对象的创建。

    3.不能被拷贝的对象

    3.1 拷贝构造函数声明 + 私有

    • C++98的写法
    class NoneCopy
    {
    public:
    private:
    	NoneCopy(const NoneCopy& v);
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3.2 delete

    • C++11的写法
    class NoneCopy
    {
    public:
    	NoneCopy(const NoneCopy& v) = delete;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

    细节:这里的编译器不会再生成默认构造函数,因为写了拷贝构造(即使是声明也算写了)。

    4.不能被继承的对象

    4.1语法层面被禁用

    • C++11——final
    class NoneInherit final// 语法层面禁用
    {
    	
    };
    //class A : public NoneInherit
    //{
    //
    //};
    //写出来直接报错
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    4.2应用层面被禁用

    • 构造函数私有
    class NoneInherit 
    {
    private:
    	NoneInherit ()
    	{
    		//...
    	}
    	NoneInherit (const NoneInherit & n)
    	{
    		//...
    	}
    };
    //class A : public NoneInherit
    //{
    //
    //};
    //不会报错,只会在实例化的时候进行报错。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    5.只能被单个使用的类

    • 所采用的模式为单例模式,简而言之就是一个程序的一个类最多只有一个对象。

    5.1饿汉模式

    • 简而言之就是开局就初始化,连主程序都没执行的时候就需要初始化,说明对象太"饿"了。
    class Hungry
    {
    public:
    	static Hungry& GetInstance()
    	{
    		return _s;
    	}
    	//进行添加数据的操作
    	void AddInstance(string str)
    	{
    		_infor.push_back(str);
    	}
    	void Print()
    	{
    		for (auto& e : _infor)
    		{
    			cout << e << " ";
    		}
    		cout << endl;
    	}
    private:
    
    	//1.将构造函数私有化
    	Hungry()
    	{}
    
    	//3.拷贝构造进行删除
    	Hungry(const Hungry& s) = delete;
    	//赋值为什么要删除?可以不用删除吗?
    	//Hungry& operator=(Hungry s) = delete;
    
    
    	vector<string> _infor;//方便测试
    	//2.定义一个static
    	static Hungry _s;//这里只是声明
    	//Hungry _s;从原理上看这里会陷入死循环。
    };
    Hungry Hungry::_s ;//这里是定义。
    //细节:由于_s的作用域是类域不是全局域,因此可以调用私有的构造函数。
    int main()
    {
    	Hungry::GetInstance().AddInstance("hello");
    	Hungry::GetInstance().AddInstance("world");
    	Hungry::GetInstance().AddInstance("hello");
    	Hungry::GetInstance().Print();
    	return 0;
    }
    
    • 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
    • 缺陷:
    1. 大量的饿汉对象进行初始化会导致主程序的启动速度放慢。
    2. 饿汉与饿汉对象之间可能存在着依赖关系,初始化时,顺序不明确,会导致出错。

    5.2懒汉模式

    • 简而言之就是用的时候才初始化,主打的就是一个懒。
    class Lazy
    {
    public:
    	static Lazy& GetInstance()
    	{
    		//判断一下对象是否为空指针
    		if (_lazy == nullptr)
    			_lazy = new Lazy;
    
    		return *_lazy;
    	}
    	//需要手动进行释放
    	static void DestroyInstance()
    	{
    		delete _lazy;//先调用析构函数对资源进行处理 再对空间进行释放。
    		_lazy = nullptr;
    	}
    	~Lazy()
    	{
    		//对资源进行处理——这里只是举个样例:将资源写到文件中。
    		FILE* fout = fopen("text1.txt", "a");
    		for (auto& e : _infor)
    		{
    			fputs(e.c_str(), fout);
    			fputs(" ", fout);
    		}
    		fclose(fout);
    	}
    	//进行添加数据的操作
    	void AddInstance(string str)
    	{
    		_infor.push_back(str);
    	}
    	void Print()
    	{
    		for (auto& e : _infor)
    		{
    			cout << e << " ";
    		}
    		cout << endl;
    	}
    private:
    	//1.将构造函数私有化
    	Lazy()
    	{}
    	//3.拷贝构造进行删除
    	Lazy(const Lazy& s) = delete;
    	//赋值
    	Lazy operator = (const Lazy& v) = delete;
    private:
    	vector<string> _infor;
    	//2.定义一个static
    	static Lazy* _lazy;//这里只是声明
    };
    //懒汉模式是由指针管,最后还需要进行手动释放,
    //此时我们可以运用RAII思想进行自动释放。
    class Gc
    {
    public:
    	~Gc()
    	{
    		Lazy::DestroyInstance();
    	}
    };
    Gc g;//全局对象在程序结束时自动调用析构函数进行清理资源。
    //程序结束也应该调用析构函数,这里是手动释放,此时可使用RAII思想
    
    int main()
    {
    	Lazy::GetInstance().AddInstance("hello");
    	Lazy::GetInstance().AddInstance("world");
    	Lazy::GetInstance().AddInstance("xxxx");
    	Lazy::GetInstance().Print();
    	Lazy::DestroyInstance();
    	Lazy::GetInstance().AddInstance("dict");
    	Lazy::GetInstance().AddInstance("single");
    	Lazy::GetInstance().AddInstance("hungry");
    	Lazy::GetInstance().Print();
    	return 0;
    }
    
    • 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

    细节:最后如果交给Gc可不用手动释放,否则还需要手动释放,其次这里的懒汉模式可以在中途对资源进行清理再进行使用(用的不多)。

    • 缺陷:涉及多线程加锁问题,等博主学了再来填坑。

    总结

      今天的分享就到此结束了,有错误请及时纠正,我是舜华,期待与你的下一次相遇!

  • 相关阅读:
    基于改进遗传算法把最优功率损耗降至最低(Matlab代码实现)
    计算机算法分析与设计(4)---矩阵连乘问题(含C++代码)
    Vue核心 MVVM模型 数据代理
    PyTorch中collate_fn的应用
    实现公共字段自动填充 技术点:枚举、注解、AOP、反射
    【分享】7-Zip压缩包的密码可以取消吗?
    STM32:TIM通道输入捕获
    【Java 进阶篇】JavaScript DOM Document对象详解
    Fiber
    进入“终端”又名“命令提示符”、“黑窗口”的方式
  • 原文地址:https://blog.csdn.net/Shun_Hua/article/details/133886474