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;
💡 关键实践原则
- 明确限制策略
在设计时决定是单例还是有限实例,以及如何处理边界情况(如超过限制时抛出异常还是返回nullptr) - 考虑所有权和生命周期
使用智能指针管理实例,确保异常安全且避免内存泄漏 - 线程安全是关键
多线程环境下,必须使用原子操作或互斥锁保护计数变量 - 防止拷贝和移动
删除拷贝构造函数和赋值操作符,避免意外创建新实例
对象数量限制模式选择:
// 策略模式选择限制方式 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、智能指针和线程安全原语可以构建健壮的对象数量限制机制,从而提升代码的可靠性和可维护性。