前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++特殊类

C++特殊类

作者头像
二肥是只大懒蓝猫
发布2023-03-30 14:53:24
3300
发布2023-03-30 14:53:24
举报
文章被收录于专栏:热爱C嘎嘎

目录

1请设计一个类,不能被拷贝

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

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

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

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


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

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

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

代码语言:javascript
复制
//C++98
class CopyBan
{
	//...
private:
	CopyBan(const CopyBan&);
	CopyBan& operator=(const CopyBan&);
	//...
};

①设置成私有之后,如果只声明没有设置成private,用户自己如果在类外定义了,就可以不能禁止拷贝了。

②只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。

在C++11中,C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数。

代码语言:javascript
复制
//C++11
class CopyBan
{
	//...
	CopyBan(const CopyBan&) = delete;
	CopyBan& operator=(const CopyBan&) = delete;
	//...
};

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

实现方法:

只能在堆上创建对象,意味着我们需要禁止产生栈对象,只能在堆上创建对象。由于在栈上的对象会自动析构,因此把析构函数私有化。

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

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

代码语言:javascript
复制
//只能在堆上创建对象的类
class HeapOnly
{
public:
	//通过静态成员函数,在堆上完成对象的创建
	static HeapOnly* CreateObj()
	{
		return new HeapOnly;
	}

	void Destory()
	{
		this->~HeapOnly();
	}
private:
	~HeapOnly()
	{}

	//将构造函数和拷贝构造私有
	HeapOnly()
	{}

	HeapOnly(const HeapOnly&) = delete;
};

int main()
{
	//创建失败,因为构造函数HeapOnly()私有了
	// //这个对象除了会调用构造函数,还会调用析构函数
	//HeapOnly hp1;
	//HeapOnly* php2 = new HeapOnly;
	//static HeapOnly hp3;

	//通过调用静态成员函数,创建堆上的对象
	HeapOnly* php4 = HeapOnly::CreateObj();

	//拷贝失败,因为拷贝构造函数HeapOnly(const HeapOnly&)被删除了
	//HeapOnly hp5(*php4);
	//delete php4;
	php4->Destory();
	return 0;
}

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

产生堆对象的唯一方法是new出来,我们只需把new的重载和delete重载给禁止掉了🆗了。同时我们把构造函数私有化,让用户只能通过特定的方式去创建对象,从而保证了类的安全性和完整性,也能防止外部直接创建该类对象,导致出现不合法的对象。

代码语言:javascript
复制
//只能在栈上创建对象
class StackOnly
{
public:
	static StackOnly Createboj()
	{
		return StackOnly();
	}

	void Print() const
	{
		cout << "StackOnly::Print()" << endl;
	}

	void* operator new(size_t size) = delete;
	void operator delete(void* p) = delete;
private:
	StackOnly()
	{}

};

int main()
{
	//new 出来的是堆上的,已经被删除了
	//StackOnly sq = new StackOnly();

	StackOnly sq1 = StackOnly::Createboj();
	StackOnly::Createboj().Print();
	sq1.Print();
	const StackOnly& so = StackOnly::Createboj();
	so.Print();
	return 0;
}

4.请设计一个类,不能被继承

子类继承父类的时候,子类中父类部分需要去调用父类的构造函数。

在C++98的方式中,我们可以将构造函数私有化,子类中调不到父类的构造函数。则无法继承。

代码语言:javascript
复制
//C++98
class NonInerit
{
public:
	static NonInerit GetInstance()
	{
		return NonInerit();
	}
private:
	NonInerit()
	{}
};

在C++11中,可以直接使用final关键字修饰类,这个类就不能被继承了。

代码语言:javascript
复制
class NonInerit final
{
	//....
};

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

单例模式

5.1饿汉模式

饿汉模式就是不管将来使不使用,在main函数之前就创建了一个对象。采用静态成员变量方法,在类中声明,在类外定义。这样就能在程序开始之前创建了一个对象。然后构造函数,拷贝和赋值都私有删除,提供特定的获取对象方法。

代码语言:javascript
复制
//饿汉模式
class InfoSingleton
{
public:
	//获取对象
	static InfoSingleton& GetInstance()
	{
		return _sins;
	}

	void Insert(string name, int salary)
	{
		_info[name] = salary;
	}

	void Print()
	{
		for (auto kv : _info)
		{
			cout << kv.first << ": " << kv.second << endl;
		}
		cout << endl;
	}
private:
	InfoSingleton()
	{}

	InfoSingleton(const InfoSingleton& info) = delete;
	InfoSingleton& operator=(const InfoSingleton& info) = delete;

	//用于测试
	map<string, int> _info;
private:
	//声明
	static InfoSingleton _sins;
};
//定义
InfoSingleton InfoSingleton::_sins;

int main()
{
	//对象在main函数之前就已经被创建出来了。
	//因此不能创建对象
	//InfoSingleton info1;

	//获取对象后使用类的方法
	InfoSingleton::GetInstance().Insert("张三", 10000);
	//也能使用引用获取对象
	InfoSingleton& infos1 = InfoSingleton::GetInstance();
	infos1.Insert("李四", 12000);
	infos1.Insert("王五", 15000);

	//两个打印都是同一个对象的
	infos1.Print();
	InfoSingleton::GetInstance().Print();
	return 0;
}

饿汉模式的缺点是初始化时数据太多,导致启动慢,并且多个单例类有初始化依赖关系,饿汉模式无法控制。

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

5.2懒汉模式

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

代码语言:javascript
复制
//懒汉模式:第一次获取单例对象的时候创建对象
//1.对象在main函数之后才会创建,不会影响启动顺序
//2.可以主动控制创建顺序

class InfoSingleton
{
public:
	static InfoSingleton& GetInstance()
	{
		//使用双检查加锁的方式保证线程安全
		//在第一次获取单例对象的时候创建对象
		if (_psins == nullptr)
		{
			_smtx.lock();
			try
			{
				if (_psins == nullptr)
				{
					//new有可能会抛异常
					_psins = new InfoSingleton;
				}
			}
			catch (...)//捕获异常
			{
				_smtx.unlock();//解锁
				throw;//重新抛出异常
			}
			_smtx.unlock();
		}
		return *_psins;
	}
	void Insert(string name, int salary)
	{
		_info[name] = salary;
	}

	void Print()
	{
		for (auto kv : _info)
		{
			cout << kv.first << ": " << kv.second << endl;
		}
		cout << endl;
	}

private:
	//将构造函数、拷贝构造和赋值重载私有化或删除
	InfoSingleton()
	{}

	InfoSingleton(const InfoSingleton& info) = delete;
	InfoSingleton& operator=(const InfoSingleton& info) = delete;

	map<string, int> _info;
private:
	//声明
	static InfoSingleton* _psins;//单例对象
	static mutex _smtx;//锁
};
//定义
InfoSingleton* InfoSingleton::_psins = nullptr;
mutex InfoSingleton::_smtx;


int main()
{
	//不可以
	//InfoSingleton info1;

	InfoSingleton::GetInstance().Insert("张三", 100000);
	InfoSingleton& infos1 = InfoSingleton::GetInstance();
	infos1.Insert("李四", 12000);
	infos1.Insert("王五", 15000);
	infos1.Print();
	InfoSingleton::GetInstance().Print();
	return 0;
}

我们可以在获取对象的双检查加锁修改一下代码,可以使用RAII锁管理类。

代码语言:javascript
复制
//RAII锁管理类
template<class Lock>
class LockGuard
{
public:
	LockGuard(Lock& lk)
		:_lk(lk)
	{
		_lk.lock();
	}

	~LockGuard()
	{
		)lk.unlock();
	}
private:
	Lock& _lk;
};
代码语言:javascript
复制
	static InfoSingleton& GetInstance()
	{
		//使用双检查加锁的方式保证线程安全
		//在第一次获取单例对象的时候创建对象
		if (_psins == nullptr)
		{
			//这是我们简单写的
			LockGuard<mutex> lock(_smtx);
			//这是官方库中的
			//std::lock_guard<mutex> lock(_smtx);
			if (_psins == nullptr)
			{
				//new有可能会抛异常
				_psins = new InfoSingleton;
			}
			
		}
		return *_psins;
	}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-03-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.请设计一个类,不能被拷贝
  • 2.请设计一个类,只能在堆上创建对象
  • 3.请设计一个类,只能在栈上创建对象
  • 4.请设计一个类,不能被继承
  • 5.请设计一个类,只能创建一个对象(单例模式)
    • 单例模式
      • 5.1饿汉模式
      • 5.2懒汉模式
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档