特殊类的设计

目录

[一、精准控制对象创建:仅堆 / 仅栈实例化](#一、精准控制对象创建:仅堆 / 仅栈实例化)

[1. 仅允许在堆上创建对象(HeapOnly)](#1. 仅允许在堆上创建对象(HeapOnly))

[2. 仅允许在栈上创建对象(StackOnly)](#2. 仅允许在栈上创建对象(StackOnly))

二、限制类的行为:禁止拷贝、不可继承

[1. 禁止拷贝构造与赋值(CopyBan)](#1. 禁止拷贝构造与赋值(CopyBan))

[2. 不可被继承的类(NonInherit)](#2. 不可被继承的类(NonInherit))

[三、设计模式实战:单例模式(饿汉 / 懒汉)](#三、设计模式实战:单例模式(饿汉 / 懒汉))

[1. 饿汉模式:饿汉式单例](#1. 饿汉模式:饿汉式单例)

[2. 懒汉模式:懒汉式单例](#2. 懒汉模式:懒汉式单例)

四、总结


在 C++ 面向对象编程中,灵活控制类对象的创建方式、限制拷贝行为、实现单例模式等,是提升代码健壮性和设计合理性的核心技巧。本文将从 "仅堆 / 仅栈创建对象""禁止拷贝""不可继承""单例模式(饿汉 / 懒汉)" 等场景出发,详解各类特殊类的设计思路与实现细节,帮助你掌握 C++ 类设计的进阶玩法。

一、精准控制对象创建:仅堆 / 仅栈实例化

在实际开发中,我们有时需要限制对象的创建位置(比如仅允许堆上创建,避免栈溢出;或仅允许栈上创建,管控内存),核心思路是通过构造函数私有化 + 运算符禁用 实现。

1. 仅允许在堆上创建对象(HeapOnly)

要禁止栈上创建对象,关键是私有化构造函数(栈对象创建依赖公开的构造函数),同时提供静态成员函数作为 "对象创建入口",并禁用拷贝构造 / 赋值(防止通过拷贝绕开限制)。

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class HeapOnly
{
public:
    // 静态成员函数:唯一的对象创建入口,返回堆上对象的指针
    static HeapOnly* CreateObj(int a)
    {
        HeapOnly* obj = new HeapOnly; // 堆上创建对象
        obj->_a = a;
        return obj;
    }

    int get() { return _a; } // 辅助函数:获取成员变量

private:
    // 1. 构造函数私有化:禁止栈上直接创建(如 HeapOnly obj;)
    HeapOnly() : _a(0) {}

    // 2. 禁用拷贝构造(C++11):避免通过拷贝创建栈对象
    HeapOnly(const HeapOnly&) = delete;

    // 3. 禁用赋值运算符:避免对象拷贝赋值
    HeapOnly& operator=(const HeapOnly&) = delete;

    int _a; // 成员变量
};

int main()
{
    // 正确:通过静态函数获取堆对象
    HeapOnly* sp1 = HeapOnly::CreateObj(123);
    cout << sp1->get() << endl; // 输出 123

    // 错误:栈上创建被禁止(构造函数私有)
    // HeapOnly obj;

    // 错误:拷贝构造被禁用
    // HeapOnly* sp2 = new HeapOnly(*sp1);

    delete sp1; // 注意释放堆对象
    return 0;
}

核心原理:栈对象的创建会直接调用构造函数(私有化后无法调用),而静态成员函数属于类而非对象,可访问私有构造函数,从而唯一掌控对象的堆创建逻辑。

2. 仅允许在栈上创建对象(StackOnly)

要禁止堆上创建对象,需禁用 new/delete 运算符 (堆对象依赖 new 分配内存),同时私有化构造函数,通过静态函数返回栈对象(利用返回值优化,避免拷贝)。

cpp 复制代码
class StackOnly
{
public:
    // 静态函数:返回栈对象(编译器会优化拷贝,无性能损耗)
    static StackOnly CreateObj(int a = 0)
    {
        return StackOnly(a);
    }

    void PrintA() const { cout << "_a = " << _a << endl; }

    // 禁用 new/delete 及其数组版本:彻底禁止堆创建
    void* operator new(size_t size) = delete;
    void operator delete(void* ptr) = delete;
    void* operator new[](size_t size) = delete;
    void operator delete[](void* ptr) = delete;

private:
    // 构造函数私有化:避免直接创建(必须通过 CreateObj)
    StackOnly(int a = 0) : _a(a) {}

    // 禁用所有拷贝/移动语义:防止绕开限制
    StackOnly(const StackOnly&) = delete;
    StackOnly& operator=(const StackOnly&) = delete;
    StackOnly(StackOnly&&) = delete;
    StackOnly& operator=(StackOnly&&) = delete;

    int _a;
};

int main()
{
    // 正确:栈上创建对象
    StackOnly obj1 = StackOnly::CreateObj(10);
    obj1.PrintA(); // 输出 _a = 10

    // 错误:堆创建被禁用(new 运算符已删除)
    // StackOnly* ptr = new StackOnly::CreateObj(20);

    return 0;
}

核心原理new 运算符底层会调用 operator new,禁用该运算符后,无法通过 new 分配堆内存;静态函数返回栈对象时,编译器的 "返回值优化(RVO)" 会直接在调用方栈上构造对象,无需拷贝。

二、限制类的行为:禁止拷贝、不可继承

1. 禁止拷贝构造与赋值(CopyBan)

某些场景下(如单例、资源独占类),需要禁止对象拷贝,核心是私有化并删除拷贝构造和赋值运算符 (C++11 后推荐用 = delete)。

cpp 复制代码
class CopyBan
{
public:
    CopyBan() {} // 构造函数公开,允许创建对象

private:
    // 禁用拷贝构造和赋值运算符
    CopyBan(const CopyBan&) = delete;
    CopyBan& operator=(const CopyBan&) = delete;
};

int main()
{
    CopyBan obj1;
    // 错误:拷贝构造被禁用
    // CopyBan obj2 = obj1;
    // 错误:赋值运算符被禁用
    // CopyBan obj3;
    // obj3 = obj1;
    return 0;
}

2. 不可被继承的类(NonInherit)

C++ 中没有原生的 final 关键字(C++11 后支持),传统方式是将构造函数私有化------ 子类构造时需调用父类构造函数,私有化后子类无法访问,从而禁止继承。

cpp 复制代码
class NonInherit
{
public:
    // 静态函数:获取类对象(唯一创建入口)
    static NonInherit CreateObj()
    {
        return NonInherit();
    }

private:
    // 构造函数私有化:子类无法调用,禁止继承
    NonInherit() {}
};

// 错误:子类无法访问父类私有构造函数
// class Derived : public NonInherit {};

int main()
{
    NonInherit obj = NonInherit::CreateObj();
    return 0;
}

补充 :C++11 后可直接用 final 关键字(更简洁):

cpp 复制代码
class NonInherit final {};

三、设计模式实战:单例模式(饿汉 / 懒汉)

单例模式确保一个类只有一个实例,并提供全局访问点。根据实例初始化时机,分为 "饿汉模式"(程序启动时初始化)和 "懒汉模式"(首次使用时初始化)。

1. 饿汉模式:饿汉式单例

"饿汉" 即 "迫不及待",在程序入口前完成单例对象初始化,优点是线程安全(无需加锁),缺点是可能提前占用资源。

cpp 复制代码
class Singleton
{
public:
    // 全局访问点:返回单例对象的引用(避免拷贝)
    static Singleton& GetInstance()
    {
        return *_inst;
    }

private:
    // 1. 构造函数私有化 + 禁用拷贝:防止外部创建/拷贝
    Singleton() {}
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    // 2. 静态指针:存储单例对象(程序启动时初始化)
    static Singleton* _inst;
};

// 全局静态成员初始化:程序入口前完成,线程安全
Singleton* Singleton::_inst = new Singleton;

int main()
{
    // 获取单例对象
    Singleton& obj1 = Singleton::GetInstance();
    Singleton& obj2 = Singleton::GetInstance();
    // 验证:obj1 和 obj2 是同一个对象
    cout << &obj1 << " " << &obj2 << endl; // 地址相同
    return 0;
}

2. 懒汉模式:懒汉式单例

"懒汉" 即 "懒加载",首次调用 GetInstance 时才初始化单例对象,优点是按需分配资源,缺点是多线程下需加锁保证线程安全(双检查锁优化)。

cpp 复制代码
#include <mutex> // 引入互斥锁

class Singleton
{
public:
    // 全局访问点:双检查锁 + 懒加载
    static Singleton& GetInstance()
    {
        // 第一层检查:避免每次调用都加锁(提升性能)
        if (_inst == nullptr)
        {
            _mtx.lock(); // 加锁:保证线程安全
            // 第二层检查:防止多个线程同时通过第一层检查
            if (_inst == nullptr)
            {
                _inst = new Singleton;
            }
            _mtx.unlock(); // 解锁
        }
        return *_inst;
    }

private:
    // 构造函数私有化 + 禁用拷贝
    Singleton() {}
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    // 静态成员:单例指针 + 互斥锁
    static Singleton* _inst;
    static mutex _mtx;
};

// 静态成员初始化:程序启动时置空
Singleton* Singleton::_inst = nullptr;
mutex Singleton::_mtx; // 初始化互斥锁

int main()
{
    Singleton& obj = Singleton::GetInstance();
    return 0;
}

双检查锁原理

  • 第一层 if:如果单例已创建,直接返回,避免每次加锁的性能损耗;
  • 加锁后第二层 if:防止多个线程同时通过第一层检查,重复创建对象。

四、总结

本文讲解的各类特殊类设计,核心都是围绕构造函数、运算符、静态成员的灵活运用:

  • 控制对象创建位置:私有化构造函数 + 禁用 new/delete/ 拷贝;
  • 限制类行为:删除拷贝构造 / 赋值、私有化构造函数;
  • 单例模式:私有化构造 + 静态指针 + 全局访问点(饿汉提前初始化,懒汉双检查锁)。

这些技巧在框架开发、资源管控、设计模式落地等场景中广泛应用,掌握后能大幅提升 C++ 代码的设计能力和健壮性。

相关推荐
嘟嘟w2 小时前
JVM(Java 虚拟机):核心原理、内存模型与调优实践
java·开发语言·jvm
信奥卷王2 小时前
2025年9月GESPC++三级真题解析(含视频)
开发语言·c++·算法
喵了几个咪2 小时前
Golang微服务框架kratos实现Socket.IO服务
开发语言·微服务·golang
q***42052 小时前
PHP使用Redis实战实录2:Redis扩展方法和PHP连接Redis的多种方案
开发语言·redis·php
qq_433554543 小时前
C++ 稀疏表
开发语言·c++·算法
Bona Sun3 小时前
单片机手搓掌上游戏机(十二)—esp8266运行gameboy模拟器之编译上传
c语言·c++·单片机·游戏机
帅中的小灰灰3 小时前
C++编程观察者设计模式
数据库·c++·设计模式
z***y8623 小时前
Java数据挖掘开发
java·开发语言·数据挖掘
软件开发技术深度爱好者4 小时前
Python库/包/模块管理工具
开发语言·python