C++ 特殊类设计与单例模式解析

目录

​​​​​​​一、堆/栈专属类设计

[1. HeapOnly(只能在堆上创建的对象)](#1. HeapOnly(只能在堆上创建的对象))

方法一:通过private限制析构函数

方法二:c++11方法,通过private限制构造,通过delete限制拷贝构造和赋值

[2. StackOnly(只能在栈上创建的对象)](#2. StackOnly(只能在栈上创建的对象))

[方法:operator new 和构造函数的访问权限来限制对象的创建方式](#方法:operator new 和构造函数的访问权限来限制对象的创建方式)

3.Copyban(不能copy的对象)

方法一:c++11玩法,通过delete来禁止拷贝构造和赋值

方法二:通过private:来实现限制拷贝构造和赋值

4.NonInherit(不能被继承的类)

方法一:private限制构造,导致派生类到不到基类的构造

[方法二:C++11 final 关键字](#方法二:C++11 final 关键字)

[方法三:私有构造函数 + 虚继承(传统方法)](#方法三:私有构造函数 + 虚继承(传统方法))

二、单例模式实现

[1. 饿汉模式(Hungry Initialization)](#1. 饿汉模式(Hungry Initialization))

[2. 懒汉模式(Lazy Initialization)](#2. 懒汉模式(Lazy Initialization))


一、堆/栈专属类设计

1. HeapOnly(只能在堆上创建的对象)
方法一:通过private限制析构函数

代码:

cpp 复制代码
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限制拷贝构造和赋值

代码:

cpp 复制代码
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 和构造函数的访问权限来限制对象的创建方式

代码:

cpp 复制代码
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来禁止拷贝构造和赋值
cpp 复制代码
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:来实现限制拷贝构造和赋值

代码:

cpp 复制代码
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
}

结果:

cpp 复制代码
class NonInherit {
public:
    static NonInherit GetInstance() {
        return NonInherit();  // 通过静态方法返回临时对象
    }
private:
    NonInherit() {}  // 私有构造函数
};

4.NonInherit(不能被继承的类)

方法一:private限制构造,导致派生类到不到基类的构造

代码:

cpp 复制代码
class NonInherit {
public:
    static NonInherit GetInstance() {
        return NonInherit();  // 通过静态方法返回临时对象
    }
private:
    NonInherit() {}  // 私有构造函数
};

上文的漏洞代码:

cpp 复制代码
class Child : public NonInherit {  // 可以继承
public:
    Child() : NonInherit() {}      // 错误:无法访问基类私有构造
};

// 但通过中间层可以破解:
class DeceptiveChild : public NonInherit {
public:
    static DeceptiveChild Create() { 
        return DeceptiveChild();   // 调用默认构造(隐式调用基类构造)
    }
private:
    DeceptiveChild() = default;    // 隐式调用基类构造
};
方法二:C++11 final 关键字

代码:

cpp 复制代码
class A final
{
	//...
};
方法三:私有构造函数 + 虚继承(传统方法)

代码:

cpp 复制代码
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)

代码:

cpp 复制代码
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)

代码:

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

相关推荐
亲爱的马哥1 分钟前
重磅更新 | 填鸭表单TDuckX2.9发布!
java
Java中文社群2 分钟前
26届双非上岸记!快手之战~
java·后端·面试
whitepure7 分钟前
万字详解Java中的面向对象(二)——设计模式
java·设计模式
whitepure9 分钟前
万字详解Java中的面向对象(一)——设计原则
java·后端
方传旺11 分钟前
C++17 std::optional 深拷贝 vs 引用:unordered_map 查询大对象性能对比
c++
Dontla27 分钟前
Makefile介绍(Makefile教程)(C/C++编译构建、自动化构建工具)
c语言·c++·自动化
后台开发者Ethan27 分钟前
Python需要了解的一些知识
开发语言·人工智能·python
Xの哲學32 分钟前
Perf使用详解
linux·网络·网络协议·算法·架构
2301_7930868737 分钟前
SpringCloud 02 服务治理 Nacos
java·spring boot·spring cloud
奶黄小甜包37 分钟前
C语言零基础第18讲:自定义类型—结构体
c语言·数据结构·笔记·学习