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

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.xdnf.cn/news/18002.html

相关文章:

  • 云计算-K8s 实战:Pod、安全上下文、HPA 、CRD、网络策略、亲和性等功能配置实操指南
  • 天地图开发的优点
  • Leaflet赋能:WebGIS视角下的省域区县天气可视化实战攻略
  • PostgreSQL——用户管理
  • Dify 从入门到精通(第 38/100 篇):Dify 的实时协作功能
  • PIDGen!DecodeProdKey函数分析之四个断点
  • 优雅草星云物联网项目私有化定制技术解析:RS485接口与工业通讯协议-优雅草卓伊凡
  • 原码表示法、反码表示法、移码表示法、补码表示法
  • C语言基础:(十五)深入理解指针(5)
  • 牛 CDR3 单抗:抗病毒领域的 “纳米级精准导弹”
  • 类与类加载器
  • 8.16打卡 DAY43 复习日
  • 【遥感图像技术系列】遥感图像风格迁移的研究进展一览
  • Mybatis-3自己实现MyBatis底层机制
  • 【leetcode】13. 罗马数字转整数
  • python学习DAY43打卡
  • MySQL 的 DDL / DML / DQL / DCL 做一次系统梳理:概念区别→常用语法→特点与注意点→实战小例子→常见面试/坑点速记
  • redis的key过期删除策略和内存淘汰机制
  • JavaWeb开发_Day14
  • Python虚拟环境与包管理工具(uv、Conda)
  • 发文暴论!线性注意力is all you need!
  • 【LeetCode每日一题】
  • Python---异常链(Exception Chaining)
  • sharding-jdbc读写分离配置
  • Spring——关于Bean以及自动配置
  • FTP上传文件错误
  • BM25算法和传统的TF-IDF算法的区别
  • IEEEtaes.cls解析
  • Trae中`settings.json`文件的Java配置项功能详解(二)
  • 343整数拆分