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; // 程序启动时即初始化}
特点:
-
立即初始化
- 单例对象在
main()
函数执行前(全局静态变量初始化阶段)就已经创建 - 通过静态成员变量
_sinst
实现
- 单例对象在
-
不可拷贝
- 禁用拷贝构造和赋值操作
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; // 静态成员}
特点
-
延迟初始化
-
第一次调用
GetInstance()
时才创建对象
-
-
手动/自动释放
-
提供
DelInstance()
手动释放 -
通过嵌套类
GC
在程序结束时自动释放(利用静态成员析构)
-