文章目录

  • 前言
  • 一、 设计一个不能被拷贝的类
    • 1. C++98 实现方式
    • 2. C++11 实现方式
  • 二、设计一个只能在堆上创建对象的类
    • 1. 方法一:析构函数私有,提供destory接口释放资源
    • 2. 方法二:构造函数私有
  • 三、 设计一个只能在栈上创建对象的类
    • 1. 实现方式
  • 四、设计一个不能被继承的类
    • 1. C++98 实现方式
    • 2. C++11 实现方式
  • 五、设计一个只能创建一个对象(单例模式)
    • 1. 单例模式介绍
    • 2. 饿汉模式(Eager Singleton)
      • (1)饿汉模式的实现步骤
      • (2)代码解析
      • (3)饿汉模式的优缺点
    • 3. 懒汉模式(Lazy Singleton)
      • (1)为什么使用懒汉模式?
      • (2)代码解析
      • (4)防止拷贝
      • (5)对象持久化
    • 4. 饿汉VS懒汉
  • 总结


前言

今天我们一起来学习常见的特殊类怎么设计,以及了解什么是单例~


一、 设计一个不能被拷贝的类

拷贝发生在两种场景:拷贝构造函数和赋值运算符。因此,若要禁止拷贝,只需让该类不能调用这两个函数。

1. C++98 实现方式

  • 方法:将拷贝构造函数和赋值运算符 仅声明不定义,并设置为 private
  • 原因
    1. 私有化拷贝构造和赋值运算符,防止外部访问。
    2. 仅声明不定义,确保该函数不会被调用,即使成员函数内部尝试拷贝也会报错。
class CopyBan {
private:CopyBan(const CopyBan&);CopyBan& operator=(const CopyBan&);
};

2. C++11 实现方式

  • C++11 提供 = delete 语法,显式删除默认拷贝构造和赋值运算符。
class CopyBan {
public:CopyBan(const CopyBan&) = delete;CopyBan& operator=(const CopyBan&) = delete;
};

二、设计一个只能在堆上创建对象的类

1. 方法一:析构函数私有,提供destory接口释放资源

class HeapOnly
{
public:void Destroy(){delete this;}
private:~HeapOnly(){//...}
};int main()
{//HeapOnly hp1;//static HeapOnly hp2;HeapOnly* hp3 = new HeapOnly;//delete hp3;hp3->Destroy();return 0;
}

2. 方法二:构造函数私有

步骤:

  1. 构造函数私有
  2. 提供静态成员函数创建对象
  3. 禁拷贝与赋值,防止利用拷贝创建栈上的对象

具体代码和使用如下:

class HeapOnly
{
public:static HeapOnly* CreateObj(){return new HeapOnly;}
private:HeapOnly(){//...}HeapOnly(const HeapOnly& hp) = delete;HeapOnly& operator=(const HeapOnly& hp) = delete;
};int main()
{//HeapOnly hp1;//static HeapOnly hp2;//HeapOnly* hp3 = new HeapOnly;HeapOnly* hp3 = HeapOnly::CreateObj();//HeapOnly copy(*hp3);return 0;
}

三、 设计一个只能在栈上创建对象的类

1. 实现方式

步骤:

  1. 类比只能在堆上创建对象的类,先将构造函数私有
  2. 提供静态成员函数构造,不同的是只能在堆上创建对象的类new来创造,这里传值返回
  3. 为了避免这种情况:StackOnly* copy = new StackOnly(s),这样的拷贝方式创建堆上的对象,但是我们又不能禁拷贝和赋值,因此需要禁掉operator new,这样来写void* operator new(size_t) = delete

具体代码如下:

class StackOnly
{
public:static StackOnly CreateObj(){StackOnly st;return st;}
private:StackOnly(){//...}// 对一个类实现专属operator newvoid* operator new(size_t size) = delete;
};int main()
{//StackOnly hp1;//static StackOnly hp2;//StackOnly* hp3 = new StackOnly;StackOnly hp3 = StackOnly::CreateObj();StackOnly copy(hp3);//  new  operator new + 构造// StackOnly* hp4 = new StackOnly(hp3);return 0;
}

四、设计一个不能被继承的类

1. C++98 实现方式

  • 方法:将构造函数 private,防止继承。
class NonInherit {
public:static NonInherit GetInstance() {return NonInherit();}private:NonInherit() {}
};

2. C++11 实现方式

  • 方法:使用 final 关键字。
class A final {// 该类无法被继承
};

五、设计一个只能创建一个对象(单例模式)

1. 单例模式介绍

  • 保证系统中某个类只有一个实例
  • 提供全局访问点
  • 应用场景:如全局配置管理。

2. 饿汉模式(Eager Singleton)

  • 特点:程序启动时即创建实例。(main函数之前就创建)
  • 优点:线程安全。
  • 缺点:可能导致启动慢。

(1)饿汉模式的实现步骤

代码:

namespace hungry
{class Singleton{public:// 2、提供获取单例对象的接口函数, 返回静态成员变量static Singleton& GetInstance(){return _sinst; }void func();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<string, string> _dict;// 4、静态成员变量,在程序启动时就初始化static Singleton _sinst;};// 5、在类外部定义静态实例,在main函数执行前已创建Singleton Singleton::_sinst;
}

(2)代码解析

(1)构造函数私有化

Singleton() {}
  • 目的是防止外部代码通过 new 关键字创建对象,确保 Singleton 类只能在 GetInstance() 方法中创建实例。

(2)提供静态方法 GetInstance()

static Singleton& GetInstance()
{return _sinst;
}
  • 通过静态方法返回单例对象的引用,确保所有地方访问的都是同一个实例。

(3)防拷贝

Singleton(const Singleton& s) = delete;
Singleton& operator=(const Singleton& s) = delete;
  • 防止拷贝和赋值,避免创建多个 Singleton 实例。

(4)静态成员变量 _sinst

static Singleton _sinst;
  • 程序启动时(main() 执行前)就创建该实例,无论是否真的需要。

(3)饿汉模式的优缺点

✅ 优点

  1. 线程安全

    • 静态成员 _sinst 在编译时创建,天然是线程安全的,无需额外同步措施(如 mutex)。
  2. 实现简单

    • 不需要加锁,避免了懒汉模式的 double-check 加锁复杂性。
  3. 访问速度快

    • 由于实例在程序启动时就已经创建,访问时无延迟,直接返回。

❌ 缺点

  1. 浪费资源

    • 如果该单例对象初始化内容很多,而程序运行期间根本没用到,就会浪费资源,降低程序启动速度。
  2. 难以控制对象创建顺序

    • 如果多个单例对象存在依赖关系(如 A 依赖 B),可能会导致未定义行为
    • 例如:
      class A {static A a_instance;B b; // A 依赖 B
      };class B {static B b_instance;A a; // B 依赖 A
      };
      
      • 由于 _sinst 在编译期静态初始化,两个类的创建顺序是由编译器决定的,可能会出现 A 还未初始化,但 B 已经尝试访问 A 的问题。

3. 懒汉模式(Lazy Singleton)

  • 特点:第一次使用时创建实例。
  • 优点:启动快,资源按需分配。
  • 缺点:线程不安全,需要加锁。

懒汉模式(Lazy Singleton)的实现思路解析
懒汉模式是一种 单例模式(Singleton Pattern)的实现方式,其特点是 延迟创建实例,即第一次使用时才创建对象,而不是程序启动时就初始化(像饿汉模式那样)。


(1)为什么使用懒汉模式?

懒汉模式的主要优点是延迟加载,适用于 对象创建成本较高、但并不是一定会用到的情况,比如:

  • 数据库连接
  • 日志管理
  • 需要动态管理生命周期的单例(如缓存数据)

(2)代码解析

完整代码:

namespace lazy
{class Singleton{public:// 2、提供获取单例对象的接口函数static Singleton& GetInstance(){if (_psinst == nullptr){// 第一次调用 GetInstance 时创建单例对象_psinst = new Singleton;}return *_psinst;}// 释放单例对象(用于手动释放或持久化数据)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;}// 3、GC(垃圾回收)类,在程序结束时自动释放 Singletonclass 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);}fclose(fin);}// 4、防拷贝Singleton(const Singleton& s) = delete;Singleton& operator=(const Singleton& s) = delete;map<string, string> _dict;static Singleton* _psinst; // 指向单例对象的指针static GC _gc; // 静态 GC 对象,自动释放 Singleton};// 5、静态成员变量初始化Singleton* Singleton::_psinst = nullptr; // 初始化单例指针为空Singleton::GC Singleton::_gc; // 在程序退出时自动释放 Singleton
}int main()
{cout << &lazy::Singleton::GetInstance() << endl;cout << &lazy::Singleton::GetInstance() << endl;cout << &lazy::Singleton::GetInstance() << endl;// 添加数据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::GetInstance().Add({ "abc", "444" });lazy::Singleton::GetInstance().Print();// 不手动调用 DelInstance,程序结束时 GC 自动释放return 0;
}

(1)懒加载(Lazy Initialization)

static Singleton& GetInstance()
{if (_psinst == nullptr){_psinst = new Singleton;}return *_psinst;
}

特点:

  • _psinst 指针初始化为 nullptr,意味着程序启动时不会创建实例。
  • 首次调用 GetInstance() 时才创建 Singleton 实例
  • 之后每次调用 GetInstance(),返回的都是同一个对象

(2)手动释放单例
一般来说,单例是不需要去释放的,

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

static void DelInstance()
{if (_psinst){delete _psinst;_psinst = nullptr;}
}

作用:

  • 由于 Singleton 对象是动态创建的,所以需要手动释放。
  • DelInstance() 用于手动释放对象,当程序需要手动控制资源释放时可以调用。

(3)GC 机制(自动释放单例)

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

工作原理:

  • GCSingleton 内部的一个嵌套类。
  • static GC _gc; 是一个 静态成员变量,它的 析构函数会在程序退出时被调用,从而自动释放 Singleton 实例。

为什么要加 GC

  • 避免 内存泄漏,因为 Singleton 对象是 new 出来的,程序退出时如果不手动 delete,就会发生泄漏。
  • 确保 Singletonmain() 结束时释放,不会影响其他对象析构的顺序。

(4)防止拷贝

Singleton(const Singleton& s) = delete;
Singleton& operator=(const Singleton& s) = delete;
  • 防止 拷贝构造赋值运算符,保证单例模式不被破坏。

(5)对象持久化

~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);}fclose(fin);
}
  • Singleton 的析构函数中,把 _dict 数据写入文件,确保程序退出时数据不会丢失。

在这里插入图片描述

在这里插入图片描述


4. 饿汉VS懒汉

方式线程安全访问速度资源消耗适用场景
饿汉模式✅ 安全✅ 快❌ 可能浪费频繁使用的单例对象(如日志、配置管理)
懒汉模式❌ 需加锁❌ 访问有延迟✅ 只在需要时创建大量占用资源但不一定用到的单例

总结

到这里就结束啦~
谢谢大家,希望对您有所帮助~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/96697.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/96697.shtml
英文地址,请注明出处:http://en.pswp.cn/web/96697.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TupiTube,一款免费开源的 2D 动画创作工具

TupiTube&#xff0c;一款免费开源的 2D 动画创作工具 ** ** 功能 ** &#xff1a;开源、免费的 2D 动画软件&#xff0c;界面简单&#xff0c;支持逐帧动画、剪纸动画、定格动画&#xff0c;能导入素材并导出多种视频和图片格式&#xff0c;适合儿童、学生和动画爱好者入门创作…

MoE架构训练系统设计:专家并行与门控网络优化策略

点击 “AladdinEdu&#xff0c;同学们用得起的【H卡】算力平台”&#xff0c;注册即送-H卡级别算力&#xff0c;80G大显存&#xff0c;按量计费&#xff0c;灵活弹性&#xff0c;顶级配置&#xff0c;学生更享专属优惠。 摘要 混合专家&#xff08;Mixture of Experts&#xf…

使用Python爬虫,selenium和requests谁更强?

py爬虫的话&#xff0c;selenium和reqeusts谁更强&#xff0c;selenium是不是能完全取代requests? 答案基本是可以的&#xff0c;selenium适合动态网页抓取&#xff0c;因为它可以控制浏览器去点击、加载网页&#xff0c;requests则比较适合静态网页采集&#xff0c;它非常轻…

编译原理-文法压缩练习

这个任务的目标就是把一个给定的文法变得“干净”和“高效”&#xff0c;剔除所有无用的部分。根据幻灯片&#xff0c;无用的&#xff08;多余的&#xff09;规则分为两大类&#xff1a; 不可达规则&#xff1a;规则的“头”&#xff08;左部非终结符&#xff09;从起始符号出发…

GPU硬件架构和配置的理解

从公司架构理解GPU架构想象一个GPU就像一家大型科技公司&#xff0c;它的任务是处理图形和计算任务&#xff08;“干活”&#xff09;。硬件概念公司架构比喻作用和特点Platform (平台)集团公司最大的独立实体。比如谷歌Alphabet是一个集团公司&#xff0c;它旗下有谷歌、Waymo…

【硬件开发】电源抑制比PSRR

电源抑制比PSRR是电压输入量和电压输出量的比值&#xff0c;通常用dB来表示。 PSRR这个参数经常和运放&#xff0c;LDO,DCDC变换器有关联。(2 封私信 / 58 条消息) 电源抑制比(PSRR)的基础知识 - 知乎

七、卷积神经网络

目录 7.1 整体结构 7.2 卷积层 7.2.1 全连接层存在的问题 7.2.2 卷积运算 7.2.3 填充 7.2.5 3维数据的卷积运算 7.2.6 结合方块思考 7.2.7 批处理 7.3 池化层 7.4 卷积层和池化层的实现 7.4.1 4维数组 7.4.2 基于 im2col的展开 7.4.3 卷积层的实现 7.4.4 池化层的…

加餐加餐!烧烤斗破苍穹

忽然起了吃烧烤的念头&#xff0c;便掏出手机点了一堆。不过二十分钟&#xff0c;外卖小哥便按响了门铃&#xff0c;手里提着一个方正的纸袋&#xff0c;还冒着热气。我将烧烤一一取出&#xff0c;排在茶几上。肉串油光发亮&#xff0c;韭菜翠绿间点缀着蒜蓉&#xff0c;茄子剖…

搜索引擎收录网站带www和不带www有区别吗?

这是一个非常常见且重要的问题。简单直接的回答是&#xff1a;有区别&#xff0c;但对搜索引擎来说&#xff0c;处理得当就不会重复&#xff1b;处理不当则会造成严重重复和权重分散。下面我为您详细解释一下&#xff0c;并提供正确的处理方法。核心区别&#xff1a;两个不同的…

AFSim2.9.0学习笔记 —— 2、AFSim的Wizard软件概述(ArkSIM集成开发环境 (IDE))

&#x1f514; AFSim2.9.0 相关技术、疑难杂症文章合集&#xff08;掌握后可自封大侠 ⓿_⓿&#xff09;&#xff08;记得收藏&#xff0c;持续更新中…&#xff09; 若还没有下载AFSim2.9.0完整软件或源码&#xff0c;请先进入本人另篇文章了解下载。 正文 ▪️主界面 打开 Ar…

建自己的Python项目仓库,使用工具:GitHub(远程仓库)、GitHub Desktop(版本控制工具)、VSCode(代码编辑器)

结合 GitHub&#xff08;远程仓库&#xff09;、GitHub Desktop&#xff08;版本控制工具&#xff09;、VSCode&#xff08;代码编辑器&#xff09; 三个工具&#xff0c;以下是更具体的Python项目仓库搭建流程&#xff0c;包含工具协同操作的详细步骤&#xff1a; 一、整体流程…

iDEA Lombok 失效 和 slf log 变量失效问题

1. lombok 失效&#xff1a;检查下配置有没有使用注解处理器&#xff1b;且这个处理中有没有带上版本&#xff1b;版本号需要与上面引入的依赖版本一致。2. 对于找不到 log 变量的操作&#xff0c;则是使用下面将这个变量使用下面的代码定义出来&#xff1b;上面去掉 slf4j注解…

go资深之路笔记(二) sync.Pool

一、 使用 sync.Pool 减少 GC 压力&#xff0c;提升性能 简单讲下go的gc&#xff0c;它的核心原理就是三色标记法和写屏障&#xff0c;可以实现优秀并发处理。gc一般不会频繁调用&#xff0c;他是根据GOGC的值来判断&#xff0c;具体就是上次触发GC后总堆值大于等于上次的(1GO…

【面试笔记-Java开发岗】

目录&#xff1a;1. synchronized 和 ReentrantLock 的区别及应用场景2. HashMap 与 LinkedHashMap 的区别3. ConcurrentHashMap 的数据结构及 JDK1.7 与 JDK1.8 区别4. Spring 常用的模式及应用场景5. 事务的四大特性&#xff08;ACID&#xff09;6. 锁机制&#xff1a;行级锁…

CSS :has() 选择器详解:为什么它是“父选择器”?如何实现真正的容器查询?

一、前言 在传统的 CSS 中&#xff0c;我们只能根据元素的自身属性、类名、ID 或其子元素/兄弟元素来设置样式&#xff0c;却无法根据其父元素或后代元素的状态来改变自身样式。 直到 :has() 选择器的出现&#xff0c;这一局面被彻底改变。 :has() 被称为 “父选择器” 或 “…

李宏毅 Deep Learning

感谢李宏毅老师qwq1. 基础概念1.1 Machine Learning问题引出&#xff1a;预测后面几天的观看人数&#xff1b;初步构建模型&#xff1a;拟合效果不好&#xff0c;就是在原数据上平移了一段距离&#xff1b;此处构建模型的本质&#xff1a;利用特征工程&#xff0c;将“多维特征…

【AI论文】分享即关爱:基于集体强化学习经验共享的高效语言模型(LM)后训练方法

摘要&#xff1a;利用强化学习&#xff08;RL&#xff09;对语言模型&#xff08;LMs&#xff09;进行后训练&#xff0c;无需监督微调即可增强其复杂推理能力&#xff0c;DeepSeek-R1-Zero便证明了这一点。然而&#xff0c;要有效利用强化学习训练语言模型&#xff0c;需要进行…

工业网关在汽车冲压车间的应用:EtherNet/IP转EtherCAT集成实践

在汽车零部件冲压车间中&#xff0c;生产线的高效协同与精准控制是提升整体产能的关键。随着自动化设备的多样化&#xff0c;不同协议的设备之间的通信成为技术难点。例如&#xff0c;罗克韦尔PLC通常采用EtherNet/IP协议&#xff0c;而许多高性能机械臂则依赖EtherCAT协议。如…

【底层机制】【C++】std::move 为什么引入?是什么?怎么实现的?怎么正确用?

C++底层机制推荐阅读 【C++基础知识】深入剖析C和C++在内存分配上的区别 【底层机制】【C++】vector 为什么等到满了才扩容而不是提前扩容? 【底层机制】malloc 在实现时为什么要对大小内存采取不同策略? 【底层机制】剖析 brk 和 sbrk的底层原理 【底层机制】为什么栈的内存…

Redis面试相关

数据过期策略 惰性删除 当用到那个key的时候再检查是否过期&#xff0c;过期则删除&#xff0c;有效则返回key 优点是可以节省检查过期的时间 缺点是会浪费内存 定期删除 每隔一段时间对一些key进行检查并且删除里面的过期key 有两种模式 slow模式是定时任务&#xff0c;频率是…