C++ 限制类对象数量的技巧与实践

在系统设计中,我们常需限制某些类的对象数量(如单例模式、资源受限场景)。本文结合《More Effective C++》条款26,剖析C++中实现该需求的核心方法与细节。

一、禁止对象创建(n=0)

若类不应产生任何对象,直接将 构造函数和拷贝构造函数声明为private,阻止外部调用:

cpp 复制代码
class NoInstances {
private:
    NoInstances();               // 私有默认构造
    NoInstances(const NoInstances&); // 私有拷贝构造
};

二、单例模式(n=1):唯一实例的经典实现

核心思路

通过 私有构造 + 静态函数返回唯一实例 ,确保全局仅一个对象。关键依赖 函数内的静态对象(而非类内静态对象):

cpp 复制代码
class Printer {
public:
    static Printer& thePrinter() { 
        static Printer p; // 函数内静态对象,首次调用时构造
        return p; 
    }
private:
    Printer() = default;                  // 私有构造
    Printer(const Printer&) = delete;    // 禁止复制
};
细节优化
  1. 命名空间隔离:避免全局命名冲突:

    cpp 复制代码
    namespace Printing {
        class Printer { /*...*/ }; 
        Printer& thePrinter() { static Printer p; return p; }
    }
    // 使用:Printing::thePrinter().reset();
  2. 函数内静态的优势

    • 类内静态对象在程序启动时构造(即使未使用),而函数内静态对象仅在首次调用时构造,符合"零代价"原则。
    • 类内静态对象的初始化顺序跨编译单元无保证(条款E47),函数内静态更安全。
  3. 避免内联陷阱

    thePrinter被声明为inline,内部静态对象可能在多编译单元中复制,导致多实例问题 。因此,非成员函数版本切勿inline

三、对象计数法(灵活控制数量)

通过类内静态变量计数,构造时检查数量,超过则抛异常:

cpp 复制代码
class Printer {
public:
    struct TooManyObjects {}; // 异常类
    Printer() {
        if (numObjects >= 1) throw TooManyObjects(); // 限制为1个
        ++numObjects;
    }
    ~Printer() { --numObjects; }
private:
    static size_t numObjects; // 计数
    Printer(const Printer&) = delete; // 禁止复制
};
size_t Printer::numObjects = 0; // 定义类静态变量
致命缺陷:继承与内嵌干扰

派生类(如ColorPrinter)或包含Printer的类(如CPFMachine)构造时,基类或内嵌的Printer构造会触发计数检查,导致误抛异常。

四、伪构造函数:灵活控制对象生灭

结合私有构造 + 静态伪构造函数,既控数量,又允许对象创建/销毁(同一时间不超量):

cpp 复制代码
class Printer {
public:
    struct TooManyObjects {};
    static Printer* makePrinter() { 
        if (numObjects >= 1) throw TooManyObjects();
        ++numObjects;
        return new Printer; 
    }
    ~Printer() { --numObjects; }
private:
    static size_t numObjects;
    Printer() = default;
    Printer(const Printer&) = delete;
};
// 配合智能指针自动销毁
unique_ptr<Printer> p(Printer::makePrinter()); 
优势
  • 仅通过makePrinter创建对象,严格控数;
  • 智能指针(如unique_ptr)自动管理销毁,避免泄漏;
  • 支持"创建→销毁→再创建"的动态场景。

总结

方法 适用场景 核心特点 注意事项
私有构造(n=0) 禁止任何对象 简单直接 -
单例(函数内静态) 程序唯一实例 延迟构造、线程安全(C++11+) 避免内联,命名空间隔离
对象计数法 固定最大数量 灵活但易受继承/内嵌干扰 谨慎处理继承和包含关系
伪构造函数 允许对象生灭的限制 动态控制,智能指针配合 需手动调用伪构造

实现时需关注静态对象初始化、命名冲突、继承陷阱,根据场景选择方案。

(注:代码基于《More Effective C++》条款26,结合现代C++特性优化。)

相关推荐
Bony-39 分钟前
Go语言完全学习指南 - 从基础到精通------语言基础篇
服务器·开发语言·golang
ShineSpark2 小时前
Crashpad 在windows下编译和使用指南
c++·windows
fl1768312 小时前
基于python的天气预报系统设计和可视化数据分析源码+报告
开发语言·python·数据分析
ACP广源盛139246256732 小时前
(ACP广源盛)GSV6172---MIPI/LVDS 信号转换为 Type-C/DisplayPort 1.4/HDMI 2.0 并集成嵌入式 MCU
c语言·开发语言·单片机·嵌入式硬件·音视频
不穿格子的程序员2 小时前
从零开始刷算法-栈-括号匹配
java·开发语言·
漂流瓶jz2 小时前
Webpack中各种devtool配置的含义与SourceMap生成逻辑
前端·javascript·webpack
这是个栗子2 小时前
【问题解决】用pnpm创建的 Vue3项目找不到 .eslintrc.js文件 及 后续的eslint配置的解决办法
javascript·vue.js·pnpm·eslint
雪域迷影3 小时前
C#中通过get请求获取api.open-meteo.com网站的天气数据
开发语言·http·c#·get
yue0083 小时前
C#类继承
java·开发语言·c#
Want5953 小时前
Python汤姆猫
开发语言·python