More Effective C++ 条款26:限制某个类所能产生的对象数量
核心思想 :通过控制类的实例化过程,限制程序中该类的对象数量,可以防止资源过度使用,确保系统资源合理分配,并实现单例或有限实例模式。
🚀 1. 问题本质分析
1.1 对象数量限制的需求:
- 单例模式:确保一个类只有一个实例,并提供全局访问点
- 有限资源管理:例如数据库连接池、线程池等,需要限制实例数量以避免资源耗尽
- 唯一性约束:某些类在逻辑上应该是唯一的,比如应用程序的配置管理器
1.2 实现限制的挑战:
- 防止直接实例化:需要拦截所有创建对象的途径(构造函数、拷贝构造、赋值操作等)
- 继承带来的复杂性:派生类可能无意中创建多个实例
- 线程安全:在多线程环境中,需要安全地控制实例数量
cpp
// 基础示例:单例模式
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 对象计数技术:
cpp
// 使用静态计数限制对象数量
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 继承条件下的限制:
cpp
// 基类限制派生类的实例数量
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 使用代理控制构造:
cpp
// 通过代理类控制实例创建
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 单例模式的变体:
cpp
// 带生命期控制的单例
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 对象计数与异常安全:
cpp
// 使用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管理有限实例:
cpp
// 对象池模式
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 线程安全的实例计数:
cpp
// 使用原子操作和互斥锁确保线程安全
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) - 考虑所有权和生命周期
使用智能指针管理实例,确保异常安全且避免内存泄漏 - 线程安全是关键
多线程环境下,必须使用原子操作或互斥锁保护计数变量 - 防止拷贝和移动
删除拷贝构造函数和赋值操作符,避免意外创建新实例
对象数量限制模式选择:
cpp// 策略模式选择限制方式 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之后):
cpp// 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、智能指针和线程安全原语可以构建健壮的对象数量限制机制,从而提升代码的可靠性和可维护性。