> 技术文档 > C++ 特殊类设计与单例模式解析

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 _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 在程序结束时自动释放(利用静态成员析构)