【c++】:特殊类设计

1. 设计一个不能被拷贝的类

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

C++98中处理

  • 将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可。

原因:

  1. 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不能禁止拷贝了
  2.  只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。

C++11

  • C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数
class BanCopy
{
public:
	BanCopy(int a = 10)
		:_a(a)
	{}

	//c++11中的写法
	BanCopy(const BanCopy& bc) = delete;
	BanCopy& operator=(const BanCopy& bc) = delete;
	
private:
	//c++98的写法
	//将拷贝构造函数与重载的 '=' 声明成私有
	/*BanCopy(const BanCopy& bc);
	BanCopy& operator=(const BanCopy& bc);*/
private:
	int _a;
};
void Test_BanCopy()
{
	BanCopy a1(15);
	BanCopy a2(a1);
	BanCopy a3(10);
	a3 = a1;
}

2. 设计一个不能被继承的类

c++98中:

  • c++98中将构造函数进行私有化,让子类无法调用父类的构造函数达到无法被继承的效果
//不能被继承的类
class BanExtend
{
private:
	//c++98中将构造函数进行私有化,让子类无法
	//调用父类的构造函数达到无法被继承的效果
	BanExtend(const char* name)
		:_name(name)
	{}
	string _name;
};
class SubClass:public BanExtend
{
public:
	SubClass(const char* name, int age)
		:BanExtend(name)
		,_age(age)
	{}

private:
	int _age;
};

 

 c++11中:

  • 用final关键字,final修饰类,表示该类不能被继承
//c++11中设置一个不能被继承的类
//只需要在类后面加一个 final 关键字进行修饰
class BanExtends final
{
private:
	BanExtends(const char* name)
		:_name(name)
	{}
	string _name;
};
class Subclass :public BanExtends
{
public:
	Subclass(const char* name, int age)
		:BanExtends(name)
		, _age(age)
	{}

private:
	int _age;
};

3. 设计一个只能在堆上创建对象的类

实现方式:

1. 将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。

注意:由于将析构函数私有化后,无法调用析构函数,我们就只能在类里面写一个调用析构函数的方法,进行自己调用。

//只能在堆上创建对象的类
class HeapOnly
{
public:
	HeapOnly(string name,int age)
		:_name(name)
		,_age(age)
	{}

	//我们需要写一个方法,进行手动释放。
	static void Delete(HeapOnly* hy)
	{
		delete hy;
	}


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

private:
	string _name;
	int _age;
};

void Test_HeapOnly()
{
	
	//HeapOnly hy1("小明",25);
	//HeapOnly hy2("xing", 14);

	//只能用new创建对象
	HeapOnly* hh1 = new HeapOnly("xxx", 15);
	//delete hh1;//由于析构函数是私有,导致delete无法调用
	hh1->Delete(hh1);
}

2. 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建

class HeapOnly
{
public:
	//提供一个公有的,获取对象的方式
	static HeapOnly* CreateObj()
	{
		return new HeapOnly;
	}
    HeapOnly(const HeapOnly& hp) = delete;
	HeapOnly& operator=(const HeapOnly& hp) = delete;
	
	//构造函数私有
private:
	HeapOnly()
		:_name("xxx")
		, _age(0)
	{}

private:
	string _name;
	int _age;
};
void Test_HeapOnly2()
{
	//无法这样写
	/*HeapOnly h("xx", 15);
	HeapOnly* hh = new HeapOnly("xx", 15);*/

	HeapOnly* hp1 = HeapOnly::CreateObj();
}

4.设计一个只能在栈上创建对象的类

方法:将构造函数私有化,然后设计静态方法创建对象返回即可
 


class StackOnly
{
public:
	//提供一个公有的,获取对象的方式
	static StackOnly CreateObj()
	{
		StackOnly st;
		return st;
	}

	//将new给禁掉
	void* operator new(size_t n) = delete;
private:
	//构造函数私有
	StackOnly()
		:_name("xxx")
		, _age(0)
	{}

private:
	string _name;
	int _age;
};

void Test_StackOnly()
{
	StackOnly st = StackOnly::CreateObj();
}

5.设计一个只能有一个对象的类(单例模式)

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

单例模式有两种实现模式:

5.1 饿汉模式

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

//饿汉模式---一开始在main函数之前就创建对象
class MemoryPool
{
public:
	static MemoryPool* GetInstance()
	{
		return _pinst;
	}
private:
	// 构造函数私有化
	MemoryPool()
	{}
	char* _ptr = nullptr;

	//static MemoryPool _inst;
	static MemoryPool* _pinst; // 声明
};
// 定义
//MemoryPool MemoryPool::_inst;
MemoryPool* MemoryPool::_pinst = new MemoryPool;

如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避
免资源竞争,提高响应速度更好
 饿汉模式的优缺点

  • 优点:简单,没有线程安全问题
  • 缺点:
  1. 一个程序中有多个单例,并且有先后创建初始化顺序要求时,饿汉模式无法控制
  2. 饿汉单例类,初始化时任务多,会影响程序启动速度。

5.2  懒汉模式

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

 

//懒汉模式
class MemoryPool
{
public:
	static MemoryPool* GetInstance()
	{
		//如果创建对象时为空,就创建一个并返回
		//如果不为空,就直接返回
		if (_pinst == nullptr)
		{
			_pinst = new MemoryPool;
		}

		return _pinst;
	}

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

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

	char* _ptr = nullptr;


	static MemoryPool* _pinst; // 声明
};
// 定义
MemoryPool* MemoryPool::_pinst = nullptr;

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

单例对象的释放问题

  1. 一般情况下,单例对象不需要释放的。因为一般整个程序运行期间都可能会用它。单例对象在进程正常结束后,也会资源释放。
  2. 有些特殊场景需要释放,比如单例对象析构时,要进行一些持久化(往文件、数据库写)操作。这个时候的解决方案就是像上面一样写一个内部类的回收类,定义一个回收对象,在main函数结束后,他会调用析构函数,就会释放单例对象
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值