C++之特殊类的设计(包含设计模式)

发布于:2022-11-28 ⋅ 阅读:(308) ⋅ 点赞:(0)

🌈前言

本篇文章进行C++中特殊类的学习!!!


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

  • 拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载

  • 因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可

C++98实现

class CopyBan
{
	// C++98中只需要将拷贝构造和赋值拷贝设为私有即可
private:
	CopyBan(const CopyBan&);
	CopyBan& operator=(const CopyBan&);
};
  1. 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不能禁止拷贝了

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


C++11实现

class CopyBan
{
	// C++11新增delete新的使用方法,可以使用delete删除该成员函数
private:
	CopyBan(const CopyBan&) = delete;
	CopyBan& operator=(const CopyBan&) = delete;
};
  • C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数

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

实现方法:

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

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

class HeapOnly
{
public:
	static HeapOnly* CreObj()
	{
		return new HeapOnly;
	}
private:
	HeapOnly() = default;
	HeapOnly(const HeapOnly&) = delete;
};

void Test()
{
	// HeapOnly hp;
	// static HeapOnly hp2;
	// HeapOnly* ph3 = new HeapOnly;
	HeapOnly* ph = HeapOnly::CreObj();
}

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

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

  • 方法二:将类中的专属new和delete函数屏蔽或删除,这样以来外部就不能通过new创建对象了

注意:也要防止定位new

class StackOnly
{
public:
	static StackOnly CreObj()
	{
		// 部分编译器会优化成直接调用构造函数,实际上要调用"构造函数" -> "拷贝构造函数" -> "拷贝构造函数"
		return StackOnly();
	}
private:
	// 方法一:
	StackOnly() = default;
	StackOnly& operator=(const StackOnly&) = delete;

	// 方法二:
	void* operator new(size_t) = delete;
	void operator delete(void*) = delete;
};

void Test()
{
	StackOnly s = StackOnly::CreObj();
}

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

C++98:将构造函数设为私有即可,派生类初始化对象必须先调用基类构造

// C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承
class NonInherit
{
public:
	static NonInherit CreObj()
	{
		return CreObj();
	}
private:
	NonInherit() 
	{}
};

//class NonInheritPlus : public NonInherit
//{};

C++11方法:final关键字,final修饰类,表示该类不能被继承

// 请设计一个类,不能被继承
class NonInherit final
{};

//class NonInheritPlus : public NonInherit
//{};

🚅5、单列模式

设计模式(Design Pattern):是一套反复使用、多数人知晓的、经过分类的、代码设计经验的总结

设计模式的目的:

  1. 为了代码的重用性、让代码更容易被他人理解、保证代码可靠性

  2. 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样


什么是单例设计模式?

  • 一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享

  • 比如:在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单列对象统一读取,然后服务器进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了再复杂环境下的管理

单例设计模式分为二种:饿汉模式和懒汉模式,下面进行介绍


🚆5.1、饿汉模式

饿汉模式:程序启动时,自动创建一个唯一的实例对象

实现方法:

  • 类中的构造函数和赋值拷贝函数私有化,这样外部就不能创建对象了

  • 在类中创建一个指向静态类对象的指针并且设为私有,防止外部调用其进行构造,类外面进行初始化,随后再设置一个返回该指针的静态成员函数即可

  • 这样的话,程序启动前会自动初始化该静态类对象,程序中想重新构造新的对象也会失败

namespace Hungry_Man
{
	// 单例模式:一个类只能创建一个对象,即单例模式,
	// 该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享

	// 饿汉模式 -- 程序启动(进入main函数之前)前创建(初始化)对象
	class Singleton
	{
	public:
		// 只能通过该接口进行成员的修改或调用
		static Singleton* GetInstance()
		{
			return psg;
		}

		void SetName(const std::string& _name)
		{
			name = _name;
		}

		void SetAddress(const std::string& _address)
		{
			address = _address;
		}

		void Print() const
		{
			std::cout << "name: " << name << std::endl;
			std::cout << "address: " << address << std::endl;
		}
	private:
		Singleton(std::string _name = "", std::string _address = "")
		{}
		Singleton& operator=(const Singleton&) = delete;
		static Singleton* psg; // 静态成员对象 -- 不会造成无穷的创建,因为静态成员不属于类,类外初始化
	private:
		std::string name;
		std::string address;
	};

	// 静态成员类外定义 -- 程序从上到下开始编译,main前静态成员就已初始化,只有一个实例
	Singleton* Singleton::psg = new Singleton();
	
	void test()
	{
		Singleton::GetInstance()->SetName("DEGTY");
		Singleton::GetInstance()->SetAddress("GFRVED OPRT");
		Singleton::GetInstance()->Print();
	}
}

在这里插入图片描述

优点:

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

缺点:

  • 在程序前初始化单例类对象,如果有多个单例类对象,会导致程序启动变慢

  • 如果有多个单例类对象实例启动顺序不确定


🚇5.2、懒汉模式

懒汉模式:

  • 如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化

  • 就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好

这里实现没有加锁,还没学多线程

实现方法:

  • 与饿汉模式大同小异,就是需要加锁

  • 也是将构造和赋值构造设为私有

  • 创建一个指向静态类对象的指针,在类外初始化成空

  • 写一个外部可以访问的静态成员函数,如果指针为空则构造一个新的类并且返回,不为空返回该指针

namespace Sluggard
{
	// 懒汉模式 -- 调用GetInstance()函数时进行初始化
	class InfoMgr
	{
	public:
		static InfoMgr* GetInstance()
		{
			// 通过静态成员函数进行构造,重复构造返回本身 -- 这里实际要加锁,后面听课(多线程知识)
			if (pfm == nullptr)
			{
				return new InfoMgr;
			}
			return pfm;
		}

		void SetName(const std::string& _name)
		{
			name = _name;
		}

		void SetAddress(const std::string& _address)
		{
			address = _address;
		}

		void Print() const
		{
			std::cout << "name: " << name << std::endl;
			std::cout << "address: " << address << std::endl;
		}
	private:
		// 将构造函数设为私有,把在栈,全局和堆开辟的方法禁掉
		InfoMgr() {}
		InfoMgr& operator=(const InfoMgr&) = delete;
	private:
		std::string name;
		std::string address;
		static InfoMgr* pfm;
	};
	// 静态成员初始化为空
	InfoMgr* InfoMgr::pfm = nullptr;

	void Test()
	{
		InfoMgr* p = InfoMgr::GetInstance();
		p->SetName("lyh");
		p->SetAddress("Arices Drty");
		p->Print();
	}
}

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到