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、智能指針和線程安全原語可以構建健壯的對象數量限制機制,從而提升代碼的可靠性和可維護性。