特殊类设计

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

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

C++98的方式(只声明)

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

原因:

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

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

C++11的方式(=delete)

C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上

=delete,表示让编译器删除掉该默认成员函数。

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

实现方式:

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

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

new的需要手动delete,其他两个会自动释放

解决办法1

直接把析构私有,让hp1和hp3无法自动析构

不过会引发hp2无法手动释放

解决办法

解决办法2

这样就会有可以拷贝hp2的情况(拷贝后可能在栈)

解决办法

把拷贝构造和赋值封死

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

方法:

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

因为这里的拷贝也符合了,所以就会出现以下问题(就可以在堆上开空间了)

重要知识点

解决办法:要先了解new的原理,这里new的时候不可以调用构造,但是还可以调用拷贝构造,但这里不能直接封了拷贝构造,因为StackOnly st用的是拷贝构造,new:会调用两个部分,一部分是构造一部分是operator new,之所以要调用operator new,而不直接调用malloc,是因为operator new会抛异常

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

C++98方式

// C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承

cpp 复制代码
class NonInherit

{

public:
 static NonInherit GetInstance()
 {
 return NonInherit();
 }

private:
 NonInherit()
 {}
};

C++11方法(final)

final关键字,final修饰类,表示该类不能被继承。

cpp 复制代码
class A  final

{
    // ....

};

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

设计模式:

设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的 总结。为什么会产生设计模式这样的东西呢?就像人类历史发展会产生兵法。最开始部落之间打 仗时都是人拼人的对砍。后来春秋战国时期,七国之间经常打仗,就发现打仗也是有套路的,后 来孙子就总结出了《孙子兵法》。孙子兵法也是类似。

使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模 式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。

单例模式:

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

饿汉模式

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

// 饿汉模式

// 优点:简单

// 缺点:可能会导致进程启动慢(都不知道是在初始化还是系统挂了),且如果有多个单例类对象实例启动顺序不确定。

第一步:构造函数私有(不能随意创建)

把map<string,string>设为私有

创建static变量

类外声明

2、提供获取单例对象的接口函数

创建对象

问题(需要防止拷贝)

然后就会出现一个问题,没有绝对防死可以拷贝构造对象

Singleton copy(Singleton::GetInstance());

第三步防拷贝


添加数据并打印

cpp 复制代码
 饿汉模式:一开始(main函数之前)就创建单例对象
静态的在main之前就创建了
 1、如果单例对象初始化内容很多,影响启动速度
 2、如果两个单例类,互相有依赖关系。 
 假设有A B两个单例类,要求A先创建,B再创建,B的初始化创建依赖A
class Singleton
{
public:
	// 2、提供获取单例对象的接口函数
	static Singleton& GetInstance()//instance是实例的意思
	{
		return _sinst;
	}

	//覆盖型的Add
	void Add(const pair<string, string>& kv)
	{
		_dict[kv.first] = kv.second;
	}

	void Print()
	{
		for (auto& e : _dict)
		{
			cout << e.first << ":" << e.second << endl;
		}
		cout << endl;
	}

private:
	// 1、构造函数私有
	Singleton()
	{
		// ...
	}

	// 3、防拷贝
	Singleton(const Singleton& s) = delete;
	Singleton& operator=(const Singleton& s) = delete;

	//使map全局只有唯一实例
	map<string, string> _dict;
	// ...

	//可以创建自己类型的对象,也不会套娃,因为静态的在静态区
	//静态的在main之前就创建了
	static Singleton _sinst;
};

Singleton Singleton::_sinst;

int main()
{
	//保证创建的都是一个对象
	//三个地址都一样,说明三个创建的是同一个
	cout << &Singleton::GetInstance() << endl;
	cout << &Singleton::GetInstance() << endl;
	cout << &Singleton::GetInstance() << endl;

	Singleton copy(Singleton::GetInstance());

	//添加数据
	Singleton::GetInstance().Add({ "1111","2222" });
	Singleton::GetInstance().Print();
	return 0;
}

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

大问题用懒汉解决

1、如果单例对象初始化内容很多,影响启动速度

2、如果两个单例类,互相有依赖关系。

假设有A B两个单例类,要求A先创建,B再创建,B的初始化创建依赖A,就没法用饿汉了,然后就有了下面的懒汉

懒汉模式

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

// 懒汉

// 优点:第一次使用实例对象时,创建对象。进程启动无负载。多个单例实例启动顺序自由控 制。

// 缺点:复杂

//懒汉用的static指针,main之前创建一个指针不耽误时间

//顺序可以随便控制

//单例一般不用智能指针,所以需要显示释放

// 特殊场景:1、中途需要显示释放 2、程序结束时,需要做一些特殊动作(如持久化)

改进

cpp 复制代码
//懒汉用的static指针,main之前创建一个指针不耽误时间
//顺序可以随便控制
//单例一般不用智能指针,所以需要显示释放
// 特殊场景:1、中途需要显示释放  2、程序结束时,需要做一些特殊动作(如持久化)

namespace lazy
{
	class Singleton
	{
	public:
		//改进::2
		// 2、提供获取单例对象的接口函数
		static Singleton& GetInstance()
		{
			if (_psinst == nullptr)
			{
				// 第一次调用GetInstance的时候创建单例对象
				_psinst = new Singleton;
			}

			return *_psinst;
		}

		//改进::3
		// 一般单例不用释放。
		// 特殊场景:1、中途需要显示释放  2、程序结束时,需要做一些特殊动作(如持久化)
		static void DelInstance()
		{
			if (_psinst)
			{
				delete _psinst;
				_psinst = nullptr;
			}
		}

		void Add(const pair<string, string>& kv)
		{
			_dict[kv.first] = kv.second;
		}

		void Print()
		{
			for (auto& e : _dict)
			{
				cout << e.first << ":" << e.second << endl;
			}
			cout << endl;
		}

		//改进::5,因为一个单例显示释放一次多个就要多次delete太麻烦
		//所以有了GC
		//用智能指针又没办法显示释放
		class GC
		{
		public:
			~GC()
			{
				lazy::Singleton::DelInstance();
			}
		};

	private:
		// 1、构造函数私有
		Singleton()
		{
			// ...
		}
		
		//程序结束后,把东西写入文件中(可持久化)
		~Singleton()
		{
			cout << "~Singleton()" << endl;

			//改进::4
			// 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;

		//改进::5
		static GC _gc;
	};
	//类外定义
	Singleton* Singleton::_psinst = nullptr;
	//改进::5
	Singleton::GC Singleton::_gc;
}
int main()
{
	//Singleton s1;
	//Singleton s2;

	cout << &lazy::Singleton::GetInstance() << endl;
	cout << &lazy::Singleton::GetInstance() << endl;
	cout << &lazy::Singleton::GetInstance() << endl;

	//Singleton copy(Singleton::GetInstance());

	lazy::Singleton::GetInstance().Add({ "xxx", "111" });
	lazy::Singleton::GetInstance().Add({ "yyy", "222" });
	lazy::Singleton::GetInstance().Add({ "zzz", "333" });
	lazy::Singleton::GetInstance().Add({ "abc", "333" });

	lazy::Singleton::GetInstance().Print();

	//lazy::Singleton::DelInstance();

	lazy::Singleton::GetInstance().Add({ "abc", "444" });
	lazy::Singleton::GetInstance().Print();

	//lazy::Singleton::DelInstance();

	return 0;
}
相关推荐
唐诺3 小时前
几种广泛使用的 C++ 编译器
c++·编译器
冷眼看人间恩怨4 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
红龙创客4 小时前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++
Lenyiin4 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin
yuanbenshidiaos6 小时前
c++---------数据类型
java·jvm·c++
十年一梦实验室7 小时前
【C++】sophus : sim_details.hpp 实现了矩阵函数 W、其导数,以及其逆 (十七)
开发语言·c++·线性代数·矩阵
taoyong0017 小时前
代码随想录算法训练营第十一天-239.滑动窗口最大值
c++·算法
这是我587 小时前
C++打小怪游戏
c++·其他·游戏·visual studio·小怪·大型·怪物
fpcc7 小时前
跟我学c++中级篇——C++中的缓存利用
c++·缓存
呆萌很7 小时前
C++ 集合 list 使用
c++