More Effective C++ 条款26:限制某个类所能产生的对象数量

More Effective C++ 条款26:限制某个类所能产生的对象数量


核心思想通过控制类的实例化过程,限制程序中该类的对象数量,可以防止资源过度使用,确保系统资源合理分配,并实现单例或有限实例模式。

🚀 1. 问题本质分析

1.1 对象数量限制的需求

  • 单例模式:确保一个类只有一个实例,并提供全局访问点
  • 有限资源管理:例如数据库连接池、线程池等,需要限制实例数量以避免资源耗尽
  • 唯一性约束:某些类在逻辑上应该是唯一的,比如应用程序的配置管理器

1.2 实现限制的挑战

  • 防止直接实例化:需要拦截所有创建对象的途径(构造函数、拷贝构造、赋值操作等)
  • 继承带来的复杂性:派生类可能无意中创建多个实例
  • 线程安全:在多线程环境中,需要安全地控制实例数量
// 基础示例:单例模式
class Singleton {
public:static Singleton& getInstance() {static Singleton instance;  // 局部静态变量,C++11保证线程安全return instance;}// 删除拷贝构造函数和赋值操作符Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;private:Singleton() = default;  // 私有构造函数~Singleton() = default;
};

📦 2. 问题深度解析

2.1 对象计数技术

// 使用静态计数限制对象数量
class LimitedInstances {
public:LimitedInstances() {if (count >= maxInstances) {throw std::runtime_error("Too many instances");}++count;}~LimitedInstances() {--count;}// 禁止拷贝和赋值,因为拷贝会增加实例,但这里我们不允许LimitedInstances(const LimitedInstances&) = delete;LimitedInstances& operator=(const LimitedInstances&) = delete;static int getCount() { return count; }private:static int count;static const int maxInstances = 5;  // 最大实例数
};int LimitedInstances::count = 0;

2.2 继承条件下的限制

// 基类限制派生类的实例数量
class Base {
protected:Base() {if (count >= maxInstances) {throw std::runtime_error("Too many instances");}++count;}~Base() {--count;}// 允许移动语义,但同样要控制数量?实际上移动构造不会增加计数,因为它是从现有对象构造,但我们这里禁止拷贝和移动Base(const Base&) = delete;Base(Base&&) = delete;Base& operator=(const Base&) = delete;Base& operator=(Base&&) = delete;private:static int count;static const int maxInstances = 10;
};int Base::count = 0;class Derived : public Base {// 派生类会调用Base的构造函数,因此受Base的计数限制
};

2.3 使用代理控制构造

// 通过代理类控制实例创建
class InstanceController;class LimitedClass {
private:LimitedClass() = default;  // 私有构造函数// 友元类,允许代理访问私有构造函数friend class InstanceController;
};class InstanceController {
public:LimitedClass& createInstance() {if (count >= maxInstances) {throw std::runtime_error("Too many instances");}++count;// 使用智能指针管理,这里简单返回静态实例的引用,实际可能需要更复杂的逻辑static LimitedClass instance;  // 注意:这里只是示例,实际可能需要多个实例return instance;}void releaseInstance() {--count;// 如果需要管理多个实例,则需要更复杂的逻辑}private:static int count;static const int maxInstances = 3;
};int InstanceController::count = 0;

⚖️ 3. 解决方案与最佳实践

3.1 单例模式的变体

// 带生命期控制的单例
template<typename T>
class Singleton {
public:static T& getInstance() {if (!instance) {instance = new T();}return *instance;}// 允许手动销毁单例,注意线程安全和重复销毁问题static void destroyInstance() {delete instance;instance = nullptr;}protected:Singleton() = default;virtual ~Singleton() = default;// 禁止拷贝和移动Singleton(const Singleton&) = delete;Singleton(Singleton&&) = delete;Singleton& operator=(const Singleton&) = delete;Singleton& operator=(Singleton&&) = delete;private:static T* instance;
};template<typename T>
T* Singleton<T>::instance = nullptr;// 使用示例
class MyClass : public Singleton<MyClass> {friend class Singleton<MyClass>;  // 允许Singleton访问MyClass的私有构造函数
private:MyClass() = default;
};

3.2 对象计数与异常安全

// 使用RAII管理对象计数
class InstanceCounter {
public:explicit InstanceCounter(int max) : maxInstances(max) {if (count >= maxInstances) {throw std::runtime_error("Too many instances");}++count;}~InstanceCounter() {--count;}static int getCount() { return count; }// 禁止拷贝和移动InstanceCounter(const InstanceCounter&) = delete;InstanceCounter(InstanceCounter&&) = delete;InstanceCounter& operator=(const InstanceCounter&) = delete;InstanceCounter& operator=(InstanceCounter&&) = delete;private:static int count;const int maxInstances;
};int InstanceCounter::count = 0;// 在需要限制的类中使用
class Limited {
public:Limited() : counter(5) {}  // 最多5个实例private:InstanceCounter counter;
};

3.3 使用std::unique_ptr管理有限实例

// 对象池模式
template<typename T, int MaxInstances>
class ObjectPool {
public:template<typename... Args>static std::unique_ptr<T, void(*)(T*)> acquire(Args&&... args) {if (count >= MaxInstances) {throw std::runtime_error("Too many instances");}++count;// 自定义删除器,在释放时减少计数return std::unique_ptr<T, void(*)(T*)>(new T(std::forward<Args>(args)...), [](T* ptr) {delete ptr;--count;});}static int getCount() { return count; }private:static std::atomic<int> count;  // 多线程安全
};template<typename T, int MaxInstances>
std::atomic<int> ObjectPool<T, MaxInstances>::count(0);// 使用示例
class ExpensiveResource {
public:ExpensiveResource() { /* 占用大量资源的操作 */ }void use() { /* 使用资源 */ }
};void useResource() {auto resource = ObjectPool<ExpensiveResource, 10>::acquire();resource->use();// 当resource离开作用域,自动释放并减少计数
}

3.4 线程安全的实例计数

// 使用原子操作和互斥锁确保线程安全
class ThreadSafeLimited {
public:ThreadSafeLimited() {std::lock_guard<std::mutex> lock(mutex);if (count >= maxInstances) {throw std::runtime_error("Too many instances");}++count;}~ThreadSafeLimited() {std::lock_guard<std::mutex> lock(mutex);--count;}// 禁止拷贝和移动ThreadSafeLimited(const ThreadSafeLimited&) = delete;ThreadSafeLimited(ThreadSafeLimited&&) = delete;ThreadSafeLimited& operator=(const ThreadSafeLimited&) = delete;ThreadSafeLimited& operator=(ThreadSafeLimited&&) = delete;static int getCount() {std::lock_guard<std::mutex> lock(mutex);return count;}private:static std::atomic<int> count;static const int maxInstances = 5;static std::mutex mutex;
};std::atomic<int> ThreadSafeLimited::count(0);
std::mutex ThreadSafeLimited::mutex;

💡 关键实践原则

  1. 明确限制策略
    在设计时决定是单例还是有限实例,以及如何处理边界情况(如超过限制时抛出异常还是返回nullptr)
  2. 考虑所有权和生命周期
    使用智能指针管理实例,确保异常安全且避免内存泄漏
  3. 线程安全是关键
    多线程环境下,必须使用原子操作或互斥锁保护计数变量
  4. 防止拷贝和移动
    删除拷贝构造函数和赋值操作符,避免意外创建新实例

对象数量限制模式选择

// 策略模式选择限制方式
template<typename T, template<typename> class CountingPolicy = SimpleCounting>
class LimitedObject : private CountingPolicy<T> {
public:template<typename... Args>LimitedObject(Args&&... args) : CountingPolicy<T>(std::forward<Args>(args)...) {CountingPolicy<T>::increment();  // 策略增加计数}~LimitedObject() {CountingPolicy<T>::decrement();}// 其他接口...
};// 计数策略
template<typename T>
class SimpleCounting {
protected:void increment() {if (++count > maxCount) {throw std::runtime_error("Too many instances");}}void decrement() {--count;}private:static int count;static const int maxCount = 1;  // 默认为单例
};template<typename T>
int SimpleCounting<T>::count = 0;

单例模式的线程安全实现(C++11之后)

// Meyer's Singleton: C++11保证静态局部变量初始化线程安全
class MeyerSingleton {
public:static MeyerSingleton& getInstance() {static MeyerSingleton instance;return instance;}// 删除拷贝和移动MeyerSingleton(const MeyerSingleton&) = delete;MeyerSingleton(MeyerSingleton&&) = delete;MeyerSingleton& operator=(const MeyerSingleton&) = delete;MeyerSingleton& operator=(MeyerSingleton&&) = delete;private:MeyerSingleton() = default;~MeyerSingleton() = default;
};

总结
限制类的对象数量是一种重要的设计模式,用于控制资源使用和确保系统约束。实现时需考虑实例计数、线程安全、生命周期管理和拷贝控制。

关键实现技术包括:静态计数、私有构造函数、删除拷贝和移动操作、智能指针管理,以及线程同步机制。根据需求选择单例模式或有限实例模式,并确保设计的一致性和安全性。

在现代C++中,利用RAII、智能指针和线程安全原语可以构建健壮的对象数量限制机制,从而提升代码的可靠性和可维护性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/97821.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/97821.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CMS系统维护中常见的安全威胁及防护指南!

内容管理系统&#xff08;CMS&#xff09;已成为网站建设的核心工具&#xff0c;但随之而来的安全风险却常被低估。超过70%的网站使用CMS构建&#xff0c;而其中近半数曾遭遇安全漏洞威胁。作为运维人员和开发者&#xff0c;了解这些安全威胁并采取相应防护措施至关重要。 一、…

springboot knife4j 接口文档入门与实战

Spring Boot3 Knife4j 项目地址https://gitee.com/supervol/loong-springboot-study&#xff08;记得给个start&#xff0c;感谢&#xff09;Knife4j 介绍在国内 Java 开发领域&#xff0c;Knife4j 是一款广受欢迎的 API 文档工具&#xff0c;它基于 OpenAPI 规范&#xff0c;在…

Spring Boot 事务失效的八大原因及解决方案详解

在 Spring Boot 项目开发中&#xff0c;声明式事务管理通过 Transactional 注解提供了极大的便利。但许多开发者都曾遇到过事务不生效的困扰。本文将详细分析导致 Spring Boot 事务失效的八大常见情况&#xff0c;并提供相应的解决方案。1. 数据库引擎不支持事务问题分析&#…

数据结构:顺序栈与链栈的原理、实现及应用

数据结构详解&#xff1a;顺序栈与链栈的原理、实现及应用 1. 引言&#xff1a;栈的核心概念 栈&#xff08;Stack&#xff09;是一种重要的线性数据结构&#xff0c;它遵循后进先出&#xff08;Last In First Out, LIFO&#xff09;的原则。这意味着最后一个被添加到栈中的元素…

apipost 8.x 脚本循环调用接口

apipost 8.x 脚本循环调用接口背景实现先说整体逻辑&#xff1a;最后背景 上周为了找某OA 偶尔出现的诡异现象&#xff0c;需要用测试工具来压测&#xff0c;看看这个问题能否重现。以前用过Jmeter&#xff0c;但是没有装&#xff0c;正好有个国产的apipost看看如何&#xff1…

STM32 - Embedded IDE - GCC - 使用 GCC 链接脚本限制 Flash 区域

导言如上所示&#xff0c;Keil限制flash区域只需要在IROM1里将Start框框与Size框框填入具体信息即可。比如bootloader程序一般从0x8000000开始&#xff0c;大小0x10000&#xff08;64KB&#xff09;。此时&#xff0c;flash的范围被限制在0x8000000 ~ 0x800FFFF。 另外&#xf…

Jenkins和Fastlane的原理、优缺点、用法、如何选择

Jenkins 和 Fastlane 是软件开发中用于自动化流程的工具一、Jenkins实现自动化打包1.1具体实现步骤安装与配置&#xff1a;首先在服务器上安装 Jenkins&#xff0c;可以通过官方提供的安装包进行安装&#xff0c;支持多种操作系统。安装完成后&#xff0c;通过 Web 界面进行初始…

DOM常见的操作有哪些?

1.DOM文档对象模型&#xff08;DOM&#xff09;是HTML和XML文档的编程接口它提供了对文档结构化表述&#xff0c;并定义了一种方式可以使从程序中对该结构进行访问&#xff0c;从而改变文档的结构&#xff0c;样式和内容任何HTML或XML文档都可以用DOM表示一个由节点构成的层级结…

【Kubernetes】知识点3

25. 说明Job与CronJob的功能。答&#xff1a;Job&#xff1a;一次性作业&#xff0c;处理短暂的一次性任务&#xff0c;仅执行一次&#xff0c;并保证处理的一个或者多个 Pod 成功结束。CronJob&#xff1a;周期性作业&#xff0c;可以指定每过多少周期执行一次任务。26. Kuber…

LINUX-网络编程-TCP-UDP

1.目的&#xff1a;不同主机&#xff0c;进程间通信。2.解决的问题1&#xff09;主机与主机之间物理层面必须互相联通。2&#xff09;进程与进程在软件层面必须互通。IP地址&#xff1a;计算机的软件地址&#xff0c;用来标识计算机设备MAC地址&#xff1a;计算机的硬件地址&am…

目标检测定位损失函数:Smooth L1 loss 、IOU loss及其变体

Smooth L1 Loss 概述 Smooth L1 Loss&#xff08;平滑 L1 损失&#xff09;&#xff0c;是一个在回归任务&#xff0c;特别是计算机视觉中的目标检测领域&#xff08;如 Faster R-CNN, SSD&#xff09;非常核心的损失函数。 xxx 表示模型的预测值&#xff0c;yyy 表示真实值&am…

Android开发之fileprovider配置路径path详细说明

第一步在清单文件配置fileprovider属性<providerandroid:name"androidx.core.content.FileProvider"android:authorities"${applicationId}.fileprovider"android:exported"false"android:grantUriPermissions"true"><meta-d…

【ComfyUI】图像描述词润色总结

在 ComfyUI 的工作流中&#xff0c;图像反推描述词能帮我们从图像里抽取语义信息&#xff0c;但这些原始描述往往还显得生硬&#xff0c;缺乏创意或流畅性。为了让提示词更自然、更有表现力&#xff0c;就需要“润色”环节。润色节点的任务&#xff0c;不是重新生成描述&#x…

java面试中经常会问到的IO、NIO问题有哪些(基础版)

文章目录一、IO 基础与分类二、NIO 核心组件与原理三、NIO 与 BIO 的实战对比四、AIO 与 NIO 的区别五、Netty 相关&#xff08;NIO 的高级应用&#xff09;总结Java 中的 IO&#xff08;输入输出&#xff09;和 NIO&#xff08;非阻塞 IO&#xff09;是面试中的重要考点&#…

时序数据库选型指南:如何为工业场景挑选最强“数据底座”

工业4.0时代&#xff0c;工厂化身为巨大的数据生产中心。数以万计的传感器、PLC和设备每时每刻都在产生着海量的时间序列数据&#xff08;Time-Series Data&#xff09;&#xff1a;温度、压力、流速、振动、设备状态……这些带时间戳的数据是工业互联网的血液&#xff0c;蕴含…

【排序算法】冒泡 选排 插排 快排 归并

一、冒泡排序// 冒泡排序var bubbleSort function (arr) {const len arr.length;for (let i 0; i < len; i) {let isSwap false;for (let j 0; j < len - 1; j) {// 每一次遍历都要比较相邻元素的大小&#xff0c;如果满足条件就交换位置if (arr[j] > arr[j 1])…

电子病历空缺句的语言学特征描述与自动分类探析(以GPT-5为例)(中)

语言学特征刻画(特征库) 句法特征 句法特征是识别 SYN 类电子病历空缺句的核心语言学维度,其量化分析通过构建依存句法结构的形式化指标,实现对语法不完整性的客观描述。该类特征主要包括依存树不完备指标、谓词-论元覆盖率及从属连词未闭合三类核心参数,共同构成 SYN 类…

InnoDB存储引擎-事务

1. 事务概述事务可由一条简单的SQL语句组成,也可以由一组复杂的SQL语句组成. 事务是访问并更新数据库中各种数据项的一个程序执行单元. 在事务中的操作, 要么都做修改, 要么都不做. 对于 InnoDB存储引擎而言, 其默认的事务隔离级别 RR , 完全遵循和满足了事务的 ACID 特性. 1.1…

web项目的目录结构

web项目的目录结构 WEB-INF 存放class文件、jar文件和配置文件&#xff0c;对于用户来说该文件夹是不可见的WEB-INF/web.xml web应用程序的描述文件&#xff0c;用来配置资源&#xff0c;如servlet、过滤器、监听器等WEB-INF/classes 用于存放class文件&#xff0c;也是该web应…

数据结构_队列Queue(C语言实现)

一、队列的基本概念 1.队列定义 队列是一种先进先出的线性表数据结构&#xff08;First in First out&#xff09;,现实中的例子就是&#xff0c;排队购票&#xff0c;先排队的先购票&#xff0c;购完票之后直接从这个队中离开&#xff0c;后来的在这个队后面排队&#xff0c;这…