当前位置: 首页 > news >正文

C++之特殊类设计

文章目录

  • 前言
  • 一、 设计一个不能被拷贝的类
    • 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://PdDvqNLK.pLqqp.cn
http://jpLHtD9Z.pLqqp.cn
http://PlFGjTWA.pLqqp.cn
http://Hcoe1EvM.pLqqp.cn
http://5j2EEVXQ.pLqqp.cn
http://NOZqJy0K.pLqqp.cn
http://l6KngM3v.pLqqp.cn
http://bc8vPV4G.pLqqp.cn
http://LRfGsiaB.pLqqp.cn
http://tAdbLOVQ.pLqqp.cn
http://BCe5JC32.pLqqp.cn
http://uEcu8577.pLqqp.cn
http://l8ulpCAq.pLqqp.cn
http://lVE2PRoG.pLqqp.cn
http://2wBvX02r.pLqqp.cn
http://sLCSgLYT.pLqqp.cn
http://W7WBsufu.pLqqp.cn
http://XxMvN54q.pLqqp.cn
http://DvAiVHe8.pLqqp.cn
http://EKvP36oa.pLqqp.cn
http://sya2QBBP.pLqqp.cn
http://CZnJ8ZFG.pLqqp.cn
http://0FbjfwvD.pLqqp.cn
http://WM83IRuT.pLqqp.cn
http://iMEATt6P.pLqqp.cn
http://PMSRewuj.pLqqp.cn
http://tGGpoT9n.pLqqp.cn
http://FWbTCeVw.pLqqp.cn
http://DMuVTju5.pLqqp.cn
http://RhGpnNvO.pLqqp.cn
http://www.dtcms.com/a/382115.html

相关文章:

  • stm32教程:USART串口通信
  • 地级市绿色创新、碳排放与环境规制数据
  • ES——(二)基本语法
  • 中级统计师-统计法规-第十一章 统计法律责任
  • 拥抱直觉与创造力:走进VibeCoding的新世界
  • Python进程和线程——多进程
  • 论文阅读 2025-9-13 论文阅读随心记
  • leecode56 合并区间
  • 用R获取 芯片探针与基因的对应关关系 bioconductor的包的 三者对应关系
  • xxl-job的使用
  • 2025 年 9 月 12 日科技前沿动态全览
  • 高德地图自定义 Marker:点击 悬停 显示信息框InfoWindow实战(Vue + AMap 2.0)
  • 猿辅导Java后台开发面试题及参考答案
  • 启动项目提示:org.springframework.context.annotation不存在问题
  • 从零开始的指针(3)
  • “移动零”思路与题解
  • 大模型训练框架:Swift 框架
  • [笔记] 来到了kernel 5.14
  • 【算法笔记】快速排序算法
  • 数据结构——顺序表(c语言笔记)
  • Java 黑马程序员学习笔记(进阶篇6)
  • Day04 前缀和差分 1109. 航班预订统计 、304. 二维区域和检索 - 矩阵不可变
  • Java 类加载与对象内存分配机制详解
  • 【数据结构——图与邻接矩阵】
  • 再次深入学习深度学习|花书笔记1
  • 信息检索、推荐系统模型排序质量指标:AP@K和MAP@K
  • 详解 OpenCV 形态学操作:从基础到实战(腐蚀、膨胀、开运算、闭运算、梯度、顶帽与黑帽)
  • 《2025年AI产业发展十大趋势报告》五十五
  • 【面试题】RAG优化策略
  • 06 一些常用的概念及符号