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

C++ 特殊类设计与单例模式解析


目录

​​​​​​​一、堆/栈专属类设计

1. HeapOnly(只能在堆上创建的对象)

方法一:通过private限制析构函数

方法二:c++11方法,通过private限制构造,通过delete限制拷贝构造和赋值

2. StackOnly(只能在栈上创建的对象)

方法:operator new 和构造函数的访问权限来限制对象的创建方式

3.Copyban(不能copy的对象)

方法一:c++11玩法,通过delete来禁止拷贝构造和赋值

方法二:通过private:来实现限制拷贝构造和赋值

4.NonInherit(不能被继承的类)

方法一:private限制构造,导致派生类到不到基类的构造

方法二:C++11 final 关键字

方法三:私有构造函数 + 虚继承(传统方法)

二、单例模式实现

1. 饿汉模式(Hungry Initialization)

2. 懒汉模式(Lazy Initialization)


一、堆/栈专属类设计

1. HeapOnly(只能在堆上创建的对象)
方法一:通过private限制析构函数

代码:

class HeapOnly {
public:void Destroy(){delete this;  // 手动释放堆对象}
private:~HeapOnly() {}  // 析构函数私有化
};int main() 
{HeapOnly hp1;       // (1) 直接栈对象 - 编译错误static HeapOnly hp2; // (2) 静态对象 - 编译错误HeapOnly* hp3 = new HeapOnly; // (3) 堆对象 - 允许delete hp3;         // (4) 直接delete - 编译错误hp3->Destroy();       // (5) 必须通过Destroy释放return 0;
}

下面会展示三个报错,并且解答原因

(1) HeapOnly hp1(栈对象)

栈对象的析构由编译器在作用域结束时自动调用,但 ~HeapOnly() 是私有的,编译器无法访问

(2) static HeapOnly hp2(静态对象)

静态对象的生命周期持续到程序结束,由编译器自动析构,但析构函数私有,编译器无法调用

(3) HeapOnly* hp3 = new HeapOnly(堆对象)

new 只调用构造函数(默认是 public 的),析构函数仅在 delete 时调用,此时由用户通过 Destroy() 间接调用

(4) delete hp3(直接 delete)

delete 会尝试调用析构函数,但析构函数是私有的,外部代码无法访问

(5) hp3->Destroy()(正确释放方式)

Destroy() 是成员函数,可以访问私有析构函数,内部调用 delete this 完成释放

方法二:c++11方法,通过private限制构造,通过delete限制拷贝构造和赋值

代码:

class HeapOnly {
public:static HeapOnly* CreateObj() {  // 唯一创建接口return new HeapOnly;}
private:HeapOnly() {}                   // 构造函数私有化HeapOnly(const HeapOnly&) = delete;            // 禁用拷贝构造HeapOnly& operator=(const HeapOnly&) = delete; // 禁用赋值
};int main()
{// HeapOnly hp1;               // (1) 栈对象 - 编译错误// static HeapOnly hp2;        // (2) 静态对象 - 编译错误// HeapOnly* hp3 = new HeapOnly; // (3) 直接new - 编译错误HeapOnly* hp3 = HeapOnly::CreateObj(); // (4) 唯一合法创建方式// HeapOnly copy(*hp3);        // (5) 拷贝对象 - 编译错误return 0;
}

下面会展示四个报错,并且解答原因

(1) HeapOnly hp1(栈对象)
构造函数是私有的,外部无法直接调用

(2) static HeapOnly hp2(静态对象)

静态对象需要调用构造函数,但构造函数私有,报错与 hp1 相同

(3) HeapOnly* hp3 = new HeapOnly(直接new)

虽然 new 可以绕过析构限制,但构造函数私有

(4) HeapOnly* hp3 = HeapOnly::CreateObj()

静态成员函数可以访问私有构造函数,返回堆分配对象指针)

(5) HeapOnly copy(*hp3)(拷贝对象)

拷贝构造函数被 = delete 显式删除

2. StackOnly(只能在栈上创建的对象)

方法:operator new 和构造函数的访问权限来限制对象的创建方式

代码:

class StackOnly {
public:static StackOnly CreateObj() {StackOnly st;  // 合法:成员函数可访问私有构造return st;     // 返回值可能触发拷贝构造(需确保可用)}private:StackOnly() {}     // 私有构造函数void* operator new(size_t) = delete;  // 禁用堆分配
};int main() 
{// StackOnly hp1;       // (1) 直接栈对象 - 编译错误// static StackOnly hp2; // (2) 静态对象 - 编译错误// StackOnly* hp3 = new StackOnly; // (3) 堆对象 - 编译错误StackOnly obj = StackOnly::CreateObj(); // (4) 合法栈对象StackOnly copy(obj);    // (5) 拷贝构造 - 依赖编译器实现// StackOnly* hp4 = new StackOnly(obj); // (6) 堆拷贝 - 编译错误return 0;
}

下面会展示四个报错,并且解答原因

(1) StackOnly hp1(直接栈对象)

构造函数是私有的,外部无法直接调用

(2) static StackOnly hp2(静态对象)

静态对象需要调用私有构造函数

(3) StackOnly* hp3 = new StackOnly(堆对象)

operator new 被显式删除

(4) StackOnly obj = StackOnly::CreateObj()

静态方法 CreateObj() 可访问私有构造返回栈对象(可能触发拷贝/移动构造)

(5) StackOnly copy(obj)(拷贝构造)

原代码未显式定义拷贝构造,如果编译器自动生成拷贝构造,则能编译通过,但违背了"栈专属"的设计初衷

(6) new StackOnly(obj)(堆拷贝)

即使拷贝构造可用,operator new 仍被禁用,报错与 (3) 相同

3.Copyban(不能copy的对象)

代码:

方法一:c++11玩法,通过delete来禁止拷贝构造和赋值
class copyban
{
public:copyban():a(0){}copyban(const copyban& t)=delete;copyban& operator =(const copyban&)=delete;int a = 0;};
int main()
{copyban s1;copyban s2;copyban s1(0);copyban s2(s1);return 0
}

结果:

方法二:通过private:来实现限制拷贝构造和赋值

代码:

class copyban
{
public:copyban():a(0){}
private:copyban(const copyban& t);copyban& operator =(const copyban&);int a = 0;
};
int main()
{copyban s1;copyban s2;copyban s1(0);copyban s2(s1);return 0
}

结果:

class NonInherit {
public:static NonInherit GetInstance() {return NonInherit();  // 通过静态方法返回临时对象}
private:NonInherit() {}  // 私有构造函数
};

4.NonInherit(不能被继承的类)

方法一:private限制构造,导致派生类到不到基类的构造

代码:

class NonInherit {
public:static NonInherit GetInstance() {return NonInherit();  // 通过静态方法返回临时对象}
private:NonInherit() {}  // 私有构造函数
};

上文的漏洞代码:

class Child : public NonInherit {  // 可以继承
public:Child() : NonInherit() {}      // 错误:无法访问基类私有构造
};// 但通过中间层可以破解:
class DeceptiveChild : public NonInherit {
public:static DeceptiveChild Create() { return DeceptiveChild();   // 调用默认构造(隐式调用基类构造)}
private:DeceptiveChild() = default;    // 隐式调用基类构造
};
方法二:C++11 final 关键字

代码:

class A final
{//...
};
方法三:私有构造函数 + 虚继承(传统方法)

代码:

class NonInheritableBase {
private:NonInheritableBase() = default;friend class NonInherit;  // 仅允许友元类继承
};class NonInherit : virtual NonInheritableBase {  // 虚继承是关键
public:static NonInherit GetInstance() {return NonInherit();}
private:NonInherit() = default;
};// 任何尝试继承的行为:
class Child : public NonInherit {
public:Child() {}  // 错误:无法调用NonInheritableBase的私有构造
};

二、单例模式实现

简单理解就是
饿汉模式:类加载时就立即创建类实例化对象(“饿”,迫不及待)
懒汉模式:延迟初始化,只有在第一次使用时才创建类实例化对象(“懒”,用的时候再弄)

1. 饿汉模式(Hungry Initialization)

代码:

namespace hungry {class Singleton {public:static Singleton& GetInstance() {return _sinst; // 直接返回预先创建好的实例}// ...其他成员函数...private:Singleton() {} // 私有构造函数Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;map<string, string> _dict;static Singleton _sinst; // 静态成员变量};Singleton Singleton::_sinst; // 程序启动时即初始化
}

特点:

  1. 立即初始化

    • 单例对象在main()函数执行前(全局静态变量初始化阶段)就已经创建
    • 通过静态成员变量 _sinst 实现
  2. 不可拷贝

    • 禁用拷贝构造和赋值操作

2. 懒汉模式(Lazy Initialization)

代码:

namespace lazy {class Singleton {public:static Singleton& GetInstance() {if (_psinst == nullptr) { // 第一次调用时创建_psinst = new Singleton;}return *_psinst;}static void DelInstance() { // 手动释放delete _psinst;_psinst = nullptr;}private:Singleton() = default;~Singleton() { /* 持久化操作 */ }Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;static Singleton* _psinst; // 指针形式static GC _gc; // 辅助垃圾回收};Singleton* Singleton::_psinst = nullptr; // 初始为空Singleton::GC Singleton::_gc; // 静态成员
}

特点

  1. 延迟初始化

    • 第一次调用 GetInstance() 时才创建对象

  2. 手动/自动释放

    • 提供 DelInstance() 手动释放

    • 通过嵌套类 GC 在程序结束时自动释放(利用静态成员析构)

http://www.dtcms.com/a/335567.html

相关文章:

  • USB 2.0声卡
  • STL——set map
  • 机器学习--PCA降维
  • 功能强大!开源免费的视频翻译、音视频转录工具
  • LLM应用终极评估指南
  • 一键管理 StarRocks:简化集群的启动、停止与状态查看
  • 《解构WebSocket断网重连:指数退避算法的前端工业级实践指南》
  • 《PEFLL: Personalized Federated Learning by Learning to Learn》——论文阅读
  • 数据电台询价的询价要求
  • 论文阅读-Gated CRF Loss for Weakly Supervised Semantic Image Segmentation
  • 【前端面试题】JavaScript核心面试题解析
  • Linux安装及远程连接知识实践
  • 【*正确*题解|两种做法】 [JLOI2013] 洛谷 P3256 赛车[半平面交/单调栈]
  • OmniDrive论文速读
  • 在云服务器上部署springBoot+vue前后端分离项目
  • 数据结构代码分享-1 顺序表
  • 数字人视频互动技术对比
  • 云计算-k8s实战指南:从 ServiceMesh 服务网格、流量管理、limitrange管理、亲和性、环境变量到RBAC管理全流程
  • Day07 缓存商品 购物车
  • 【远程桌面】从RustDesk服务器看UDP对比WebRTC
  • es下载、安装、部署以及集成和mysql数据同步
  • 给纯小白的Python操作Word笔记
  • gin结合minio来做文件存储
  • The Network Link Layer: 无线传感器中Delay Tolerant Networks – DTNs 延迟容忍网络
  • css: word pacing属性
  • 哈希表与unorder_set,unorder_map的学习
  • Spring 源码学习(十一)—— webmvc 配置
  • 【JavaEE】多线程初阶3:死锁 + 线程安全 + volatile 关键字
  • 已开源:Highcharts.NET,Highcharts Android,与Highcharts iOS集成
  • VS Code配置MinGW64编译Apache Arrow C++库