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

文章目录

  • 1.只能创建在堆上的对象
    • 1.1析构函数私有
    • 1.2构造函数私有
  • 2.只能创建在栈上的对象
  • 3.不能被拷贝的对象
    • 3.1 拷贝构造函数声明 + 私有
    • 3.2 delete
  • 4.不能被继承的对象
    • 4.1语法层面被禁用
    • 4.2应用层面被禁用
  • 5.只能被单个使用的类
    • 5.1饿汉模式
    • 5.2懒汉模式
  • 总结

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

1.1析构函数私有

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

1.2构造函数私有

class HeapOnly
{

public:
	//static 公有成员函数可以直接通过类访问限定符进行访问。
	static HeapOnly* CreatHeapOnly()
	{
		return new HeapOnly;
	}
	//其实不加static也可以调用,想一下如何实现。
	
private:
	//防止new出来的对象再进行拷贝构造局部对象
	HeapOnly(const HeapOnly& v) = delete;
	//赋值要不要封呢?可以封,不过没有必要,因为赋值是已存在的两个对象进行的赋值操作
	//我们封的是对已存在的对象进行初始化的操作。
	//防止生成局部对象
	HeapOnly()
	{
		//...
	}
};

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*);
};
  • 简化一下:
class NoneHeap
{
public:
	//把构造私有化
	NoneHeap()
	{
		//...
	}
private:
	void* operator new(size_t);
	void operator delete(void*);
};

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

3.不能被拷贝的对象

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

  • C++98的写法
class NoneCopy
{
public:
private:
	NoneCopy(const NoneCopy& v);
};

3.2 delete

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

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

4.不能被继承的对象

4.1语法层面被禁用

  • C++11——final
class NoneInherit final// 语法层面禁用
{
	
};
//class A : public NoneInherit
//{
//
//};
//写出来直接报错

4.2应用层面被禁用

  • 构造函数私有
class NoneInherit 
{
private:
	NoneInherit ()
	{
		//...
	}
	NoneInherit (const NoneInherit & n)
	{
		//...
	}
};
//class A : public NoneInherit
//{
//
//};
//不会报错,只会在实例化的时候进行报错。

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. 饿汉与饿汉对象之间可能存在着依赖关系,初始化时,顺序不明确,会导致出错。

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;
}

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

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

总结

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

你可能感兴趣的:(C++进阶之路,c++,特殊类的设计)