当前位置: 首页 > 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.lryc.cn/news/623658.html

相关文章:

  • 编译器生成的合成访问方法(Synthetic Accessor Method)
  • Python训练营打卡Day35-复习日
  • Spring Framework :IoC 容器的原理与实践
  • 库制作与原理(下)
  • HAL-EXTI配置
  • Python异常、模块与包(五分钟小白从入门)
  • STL 容器
  • 【Linux网络编程】NAT、代理服务、内网穿透
  • Windows 10共享打印机操作指南
  • 第七十八章:AI的“智能美食家”:输出图像风格偏移的定位方法——从“滤镜病”到“大师风范”!
  • Flutter 3.35 更新要点解析
  • 解码词嵌入向量的正负奥秘
  • 【R语言】R语言矩阵运算:矩阵乘除法与逐元素乘除法计算对比
  • Flutter vs Pygame 桌面应用开发对比分析
  • SQL Server 2019安装教程(超详细图文)
  • ZKmall开源商城的移动商城搭建:Uni-app+Vue3 实现多端购物体验
  • 【Linux系统】动静态库的制作
  • 雷卯针对香橙派Orange Pi 5 Ultra开发板防雷防静电方案
  • riscv中断处理软硬件流程总结
  • AOP配置类自动注入
  • 高级堆结构
  • 机器人经验学习1 杂记
  • Ansible 管理变量和事实
  • CW32L011_电机驱动器开发板试用
  • SpringCloud 06 服务容错 Sentinel
  • 云智智慧停充一体云-allnew全新体验-路内停车源码+路外停车源码+充电桩源码解决方案
  • 中国星网发展情况全面分析
  • python实现梅尔频率倒谱系数(MFCC) 除了傅里叶变换和离散余弦变换
  • 3.逻辑回归:从分类到正则化
  • pyecharts可视化图表组合组件_Grid:打造专业数据仪表盘