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

AnyGrasp实战指南:从环境配置到抓取检测与跟踪 本文详细介绍了AnyGrasp从环境配置到抓取检测与跟踪的实战指南。涵盖硬件要求、关键依赖安装避坑、抓取检测与跟踪的实战技巧,以及常见问题解决方案和性能优化建议,帮助开发者快速掌握这一高精度物体抓取检测与跟踪技术。 阅读详情

业务处理时常常会出现一些特定的要求,需要设计出特殊的功能,下面介绍几个常见的特殊类设计

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

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

class CopyBan
{
	// ...

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

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

class CopyBan
{
	// ...
	CopyBan(const CopyBan&) = delete;
	CopyBan& operator=(const CopyBan&) = delete;
	//...
};

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

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

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.请设计一个类,只能在栈上创建对象

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;
};

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

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

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

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

class A  final
{
    // ....
};

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

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

单例模式:

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

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

🚩饿汉模式

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,引发逻辑错误或崩溃

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

🚩懒汉模式

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;

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


希望读者们多多三连支持

小编会继续更新

你们的鼓励就是我前进的动力!

请添加图片描述

LVGL在嵌入式Linux系统上的高效移植与优化实践 本文详细介绍了在嵌入式Linux系统(特别是ARM平台)上移植和优化LVGL图形库的完整实践流程。内容涵盖从开发环境搭建、代码获取、CMake工程配置,到关键配置文件定制、显示与输入设备端口移植,以及内存、渲染等核心性能优化技巧,旨在帮助开发者高效构建流畅的嵌入式UI应用。 阅读详情

相关推荐

晶晨S905L3S/S905L3SB芯片刷机实战:BL加载工具与线刷固件全解析

本文针对晶晨S905L3S/S905L3SB芯片电视盒子刷机时常见的2%失败问题,提供了两种核心解决方案。一是使用已集成特殊通信协议的专用版USB Burning Tool(如V2.2.5/V2.2.7),二是采用旧版工具配合独立的BootLoader加载工具。文章详细解析了两种方案的原理、操作步骤、固件选择及常见问题排查,旨在帮助用户成功完成刷机,让老旧设备焕发新生。

weixin_29238147的博客 1109

【内存管理】嵌入式里的禁忌之术:为什么 Malloc 是魔鬼?从堆碎片到确定性内存池的救赎之路

即使用了内存池,忘记Free也会导致泄漏。如何防御?在 PC 上,你可以信任操作系统,反正 RAM 有 32GB。在嵌入式上,信任由于无知,控制源于恐惧。不要把内存管理的权力交给编译器自带的malloc。它既不知道你的业务逻辑,也不在乎你的实时性。构建自己的内存池。知道每一块 RAM 去了哪里。知道分配它需要多少个周期。知道它永远不会碎成渣。只有当你掌控了每一个字节的生老病死,你才能说你真正掌控了这个系统。

Max_uuc的博客 1053

从零到一:桌面宠物的硬件架构设计与工程实践

本文详细介绍了基于STM32F103C8T6主控平台的桌面宠物机器人硬件架构设计与工程实践。内容涵盖电源管理、舵机控制、PCB布局及系统调试等核心环节,为硬件爱好者提供从零到一的完整开发指南,助力打造稳定可靠的智能桌面伴侣。

alice7model的博客 1113

《从监狱到重生:C++刑满释放人员再就业指南》

—论如何用祖传代码换取Offer赎罪券。

wangxu_a的博客 885

用Pixhawk 2.4.8和PX4 1.13,从零搭建你的第一支无人机编队(附避坑清单)

本文详细介绍了如何使用Pixhawk 2.4.8飞控和PX4 1.13固件从零搭建低成本无人机编队系统。涵盖硬件选型、固件配置、多机通信和编队飞行逻辑开发的全流程,特别针对学生和爱好者提供实战验证的避坑方案,帮助实现多机协同飞行。

weixin_27219587的博客 403

Jetson Nano 【1】入手开箱、烧系统

类似于半开箱哈哈 怀着小期待的心情,屁颠屁颠跑到村口领快递,拿到了这个香喷喷的Jetson Nano 开发板 开箱: 由于太激动了忘记拍点啥,于是在最后留了张全家福 板子的结构: 我觉得这个有必要记录一下,于是去官方文档拿了张图来:官方文档传送 1=SD card 的卡槽,位于第二层板子的背部,也就是大风扇屁股位置 2=40针扩展接头(还没开用) 3=USB的供电口,根据大佬测试(某哩...

椰子奶糖的博客 4196

C++复合数据类型深度解析:引用与指针的核心原理与实战应用

在C++编程中,复合数据类型是构建复杂程序的基础,它们允许开发者将基本数据类型组合成更高级的数据结构。理解内存模型是掌握复合类型的关键,变量在内存中拥有唯一的地址,而左值与右值的概念则定义了数据的持久性与可操作性。引用作为变量的安全别名,提供了高效且直观的间接访问方式,尤其在函数参数传递中能避免不必要的拷贝开销。指针则直接操作内存地址,为动态内存管理和底层资源控制提供了灵活性,但同时也带来了空指针和悬垂指针等风险。这些技术在现代软件开发中广泛应用于性能优化、数据结构实现(如链表)和系统级编程。本指南聚焦于引

weixin_33738982的博客 266

Win11笔记本BIOS设置避坑大全:Secure Boot、RST、UEFI引导,一次讲清

本文详细解析了Win11笔记本BIOS设置中的关键问题,包括Secure Boot、Intel RST和UEFI引导的配置方法,帮助用户避开双系统安装中的常见陷阱。特别针对不同品牌笔记本的BIOS界面差异,提供了实用的操作指南和优化建议,确保系统兼容性和启动效率。

weixin_29251131的博客 689

深挖C++:类和对象(中)

(先进后出)

xys_678的博客 250

ESD镜像解密与转换全攻略:从esd-decrypter到WinNTSetup的完整流程

本文详细解析了ESD镜像的加密特性与压缩优势,并提供了从解密到转换的完整实战指南。核心步骤包括使用esd-decrypter工具解密ESD文件为WIM格式,以及如何结合WinNTSetup进行灵活的系统安装或进一步封装为可引导的ISO镜像,帮助用户高效处理官方Windows安装镜像。

weixin_29207439的博客 452

Vitis Model Composer闪退?手把手教你用命令行启动Vivado 2021后的System Generator

本文详细解析了Vitis Model Composer闪退问题的根本原因,并提供了命令行启动Vivado 2021后System Generator的完整解决方案。从环境变量配置、组件缺失排查到权限问题处理,手把手教你通过命令行深度排错,并分享高级自动化脚本和系统优化技巧,帮助开发者高效使用Vitis Model Composer与System Generator进行HDL开发。

weixin_26905487的博客 349

ThunderKittens 深度解析:一个不基于 MLIR 的 C++ 模板 GPU DSL

Triton 是"让更多人能写 GPU kernel",ThunderKittens 是"让会写的人写得更爽"。前者用 MLIR 换易用性,后者用 C++ 模板换可控性。它们不是替代关系,而是覆盖了 GPU 编程光谱的两端。ThunderKittens 的出现,本质上是对"GPU DSL 必须基于 MLIR"这一默认假设的挑战。编译期类型系统本身就可以是一个强大的 DSL 基础设施,不需要中间 IR,不需要黑盒 pass,也能实现 tile 级抽象和零运行时开销。

Maxwell_Newton的博客 345

【Claude】SSL certificate verification 错误:证书校验失败与自定义 CA 配置

获取并验证企业 CA 证书后,需要配置 Claude Code(Node.js)信任该证书。Claude Code 的 SSL 错误通常源于企业代理:不是 Anthropic 服务器的问题Node.js 使用独立的 CA 存储:操作系统信任了企业 CA 不等于 Node.js 也信任NODE_EXTRA_CA_CERTS 是标准解决方案:指向包含企业 CA 的 PEM 文件绝不使用 NODE_TLS_REJECT_UNAUTHORIZED=0:这等于关闭所有 HTTPS 安全验证证书链完整性很重要。

博客 6813

C++移动构造函数详解

移动构造函数是C++11引入的一种特殊构造函数,用于从临时对象(右值)"窃取"资源来初始化新对象,而不是进行昂贵的深拷贝。char* data;public:// 移动构造函数:参数是右值引用 &&// 关键:把源对象的资源置空。

拒绝AI文,努力在AI时代写出感动人心的好文章 393

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

跟我学c++中级篇—类型定义的方式

本文系统阐述了编程中类型定义的重要性及c++中的实现方式。重点对比了宏定义、typedef与modern using三种机制,指出using在可读性、灵活性和模板支持上的优势,推荐优先使用。同时提醒开发者注意别名滥用、类型歧义、错误信息冗长等问题,强调统一规范与合理设计。最终结论:在现代c++开发中,应以using为主,兼顾简洁性与可维护性,避免陷入命名混乱与编译困境。

fpcc的专栏 86

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

C++ 函数模版

模板参数:● template ,T 就是类型占位符,名字可以自定义,习惯用T● 占位符可以在函数返回值、形参、函数内部当作类型使用使用要点模板代码一般写在头文件:编译器需要看到完整模板源码,才能在每个编译单元完成实例化全局函数、类内成员函数都可以写成模板实例化发生在编译阶段,不是运行时隐式实例化:调用时编译器自动推导实参类型,生成对应版本函数。

2501_94041724的博客 306

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

C++ 运行时多态与虚函数机制全面剖析【面试重点】

构成C++ 运行时多态必须是基类的指针或者引用调用虚函数。被调用的函数必须是虚函数,并且派生类必须对该虚函数完成重写/覆盖(Override)。只有基类的指针或引用才能既指向基类对象,又指向派生类对象(切片机制);而只有完成了虚函数重写,派生类与基类才具备不同的函数行为,达到“指向谁就调用谁”的效果。在类成员函数的前面加上virtual关键字修饰,该成员函数就成为了虚函数。public:// 成员函数前加 virtual 修饰,声明为虚函数。

Fuyo_0209的博客 447

#SQLite 数据库笔记一:(概念 / 终端指令 / SQL 语句 / C 编程 / 回调函数)

数据库 = 数据的仓库,假设数据是海量。能力说明存储数据持久化保存,程序退出数据还在统计数据、分组、排序数据管理增、删、改、查(CRUD)嵌入式设备上也会用数据库(单片机网关、路由器、工业 HMI 都常见),因为 sqlite 体积小、无需服务进程、单文件即可。数据库 = 库 > 表 > 记录(行) > 字段(列)sqlite3 = 开源、C 写的、单文件、免安装、最多 2T 的嵌入式关系型数据库。

lwq20051110的博客 234

蓝桥杯·全国青少年信息素养大赛·电子学会Python/C++考级 备考:莫队算法离线区间查询详解

备赛进行时:第十八届蓝桥杯软件赛报名已于 2026-10-08 10:00 开启、12-18 14:00 截止,省赛定在 2027-04-10(C/C++·Python 程序设计 9:00–13:00);中国电子学会 2026 年 12 月软件编程(C/C++ 一级至十级、Python 一级至六级)考级报名 9/23–12/2 16:00、统考 12-12;全国青少年信息素养大赛 2026—2027 赛季算法应用主题赛也已进入复盘练兵窗口。

IT_Scratch的博客 163

【C++ 基础入门】C++继承(下):问题、底层原理与组合

C++继承(菱形继承的问题、虚继承原理、继承与组合、构造顺序)

2601_96587588的博客 797

825 mW 顶上 212 °C:ASM491S 的 105 °C/W、250 mA 短路电流与热关断

25 °C 环境里结温 34.1 °C,125 °C 环境里结温 134.1 °C——后者已经离 150 °C 只有 16 °C 了,但还在范围内。换个角度看同一件事:从 25 °C 和从 125 °C 起算,温升都是 86.6 °C,但一个到 111.6 °C(还在范围内),一个到 211.6 °C(远远超限)。这个算例里 20 °C/W 是我假设的,实际值取决于机壳材料、密封程度、有没有风扇,跨度可以从 5 °C/W 到 50 °C/W 以上。外面 50 °C 的机箱,盒内就是 63.9 °C。

ANSILIC的博客 373

《C++ 项目:手写大模型接入 SDK》Gemini接口设计(上):初始化、全量响应实现和测试

本文系统完成了Gemini模型通过OpenAI兼容API的接入开发,基于Apifox验证接口规范,设计并实现GeminiProvider类,涵盖初始化、状态检测、全量响应等核心功能。重点处理了代理配置强制校验、Bearer鉴权、JSON序列化与响应解析等工程细节,并通过GTest与CMake完成单元测试,验证链路可用性。项目遵循策略模式,保持与DeepSeek、ChatGPT等模型的统一抽象接口,显著提升SDK可扩展性。

2501_91275995的博客 395
上一篇: C++内存列传之RAII宇宙:智能指针
下一篇: C++显性契约与隐性规则:类型转换
澪贰
澪贰 优质创作者: C/C++技术领域 优质创作者: C/C++技术领域
博客等级 码龄2年 1万+粉丝 222原创
评论 47
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值