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; // 程序启动时即初始化
}
特点:
-
立即初始化
- 单例对象在
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
在程序结束时自动释放(利用静态成员析构)
-