前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >C++设计的禁忌与救赎:特殊类设计

C++设计的禁忌与救赎:特殊类设计

作者头像
DARLING Zero two
发布2025-06-09 08:54:16
发布2025-06-09 08:54:16
6000
代码可运行
举报
文章被收录于专栏:C语言C语言
运行总次数:0
代码可运行

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

涉及拷贝的只有拷贝构造赋值重载,把这些禁用了就行了

代码语言:javascript
代码运行次数:0
运行
复制
class CopyBan
{
	// ...

private:
	CopyBan(const CopyBan&);
	CopyBan& operator=(const CopyBan&);
	//...
};

对于 C++98,直接设置为私有即可,这样类外就无法访问了,只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了

代码语言:javascript
代码运行次数:0
运行
复制
class CopyBan
{
	// ...
	CopyBan(const CopyBan&) = delete;
	CopyBan& operator=(const CopyBan&) = delete;
	//...
};

对于 C++11,直接在默认成员函数后跟上 = delete,表示让编译器删除掉该默认成员函数

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

代码语言:javascript
代码运行次数:0
运行
复制
class HeapOnly
{
public:
	static HeapOnly* CreateObject()
	{
		return new HeapOnly;
	}
private:
	HeapOnly() {}

	// C++98
	// 1.只声明,不实现。因为实现可能会很麻烦,而你本身不需要
    // 2.声明成私有
	HeapOnly(const HeapOnly&);

	// or

	// C++11    
	HeapOnly(const HeapOnly&) = delete;
};

创建对象需要构造函数,所以首先把构造函数给禁用了,还注意应该禁止拷贝构造,防止再创建,因为类内能调用构造函数,所以创建一个 public 函数提供接口

在构造函数被私有化的情况下,静态方法是唯一能够访问私有构造函数并创建对象的途径,静态方法属于类本身,而非类的某个实例,因此无需创建对象即可调用,例如: HeapOnly::CreateObject() 可直接通过类名调用

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

代码语言:javascript
代码运行次数:0
运行
复制
class StackOnly
{
public:
	static StackOnly CreateObj()
	{
		return StackOnly();
	}

	// 禁掉operator new可以把下面用new 调用拷贝构造申请对象给禁掉
 // StackOnly obj = StackOnly::CreateObj();
 // StackOnly* ptr3 = new StackOnly(obj);
	void* operator new(size_t size) = delete;
	void operator delete(void* p) = delete;
private:
	StackOnly()
		:_a(0)
	{
	}
private:
	int _a;
};

因为堆上创建销毁需要通过 newdelete,之前又学过 new -> operator new + 构造函数delete -> operator delete + 析构函数,所以直接将这些禁掉就好了

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

代码语言:javascript
代码运行次数:0
运行
复制
class NonInherit
{
public:
	static NonInherit GetInstance()
	{
		return NonInherit();
	}
private:
	NonInherit()
	{}
};

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

代码语言:javascript
代码运行次数:0
运行
复制
class A  final
{
    // ....
};

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

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

单例模式:

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

以下是单例模式的两种实现模式:

🚩饿汉模式

代码语言:javascript
代码运行次数:0
运行
复制
class Singleton
{
public:
	static Singleton* GetInstance()
	{
		return &m_instance;
	}
private:
	// 构造函数私有
	Singleton() {};

	// C++98 防拷贝
	Singleton(Singleton const&);
	Singleton& operator=(Singleton const&);

	// or

	// C++11
	Singleton(Singleton const&) = delete;
	Singleton& operator=(Singleton const&) = delete;

	static Singleton m_instance;
};

Singleton Singleton::m_instance;  // 在程序入口之前就完成单例对象的初始化

静态变量属于类而非对象,程序运行期间仅存在一份实例,且在程序入口(main())前初始化,static Singleton m_instance; 是声明,Singleton Singleton::m_instance; 是定义

饿汉模式在程序启动时(或类加载时)立即创建单例实例,若实例的初始化逻辑复杂(如: 读取大文件 / 配置(如数据库连接、配置文件) 网络请求(初始化远程服务连接) 复杂计算(预加载数据字典、缓存构建) 这些操作会阻塞进程的启动流程,导致启动时间明显增加

全局静态对象的初始化顺序问题:

C++ 中,多个全局静态对象的初始化顺序是未定义的(由编译器和链接器决定),若某个饿汉单例 A 依赖另一个全局对象 B,而 B 的初始化顺序在 A 之后,会导致 A 初始化时访问未初始化的 B,引发逻辑错误或崩溃

这类问题难以调试,且会间接增加启动阶段的异常处理开销

🚩懒汉模式

代码语言:javascript
代码运行次数:0
运行
复制
class Singleton
{
public:
	// 2、提供获取单例对象的接口函数
	static Singleton& GetInstance()
	{
		if (_psinst == nullptr)
		{
			// 第一次调用GetInstance的时候创建单例对象
			_psinst = new Singleton;
		}

		return *_psinst;
	}

	// 一般单例不用释放。
	// 特殊场景: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;
	}

	class GC
	{
	public:
		~GC()
		{
			lazy::Singleton::DelInstance();
		}
	};

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

	~Singleton()
	{
		cout << "~Singleton()" << endl;

		// 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;
	static GC _gc;
};

Singleton* Singleton::_psinst = nullptr;
Singleton::GC Singleton::_gc;

在第一次使用时才初始化单例实例,而非程序启动时。这种方式避免了不必要的资源浪费,但需要处理多线程环境下的线程安全问题

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-06-09,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

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