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

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;

💡 关键实践原则

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

对象数量限制模式选择

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、智能指针和线程安全原语可以构建健壮的对象数量限制机制,从而提升代码的可靠性和可维护性。

相关推荐
ajassi20008 小时前
开源 C++ QT Widget 开发(十一)进程间通信--Windows 窗口通信
linux·c++·windows·qt·开源
2401_858286119 小时前
CD75.【C++ Dev】异常
开发语言·c++·异常
蒹葭玉树9 小时前
【C++上岸】C++常见面试题目--算法篇(第十八期)
c++·算法·面试
郝YH是人间理想11 小时前
408考研——单链表代码题常见套路总结
c语言·数据结构·c++·考研·链表
hansang_IR11 小时前
【线性代数基础 | 那忘算9】基尔霍夫(拉普拉斯)矩阵 & 矩阵—树定理证明 [详细推导]
c++·笔记·线性代数·算法·矩阵·矩阵树定理·基尔霍夫矩阵
XZHOUMIN13 小时前
66解决通过南瑞加密网关传输文件和推送视频的失败的问题
c++·音视频
君鼎14 小时前
More Effective C++ 条款19:理解临时对象的来源(Understand the Origin of Temporary Objects)
c++
LoveXming15 小时前
Chapter1—设计模式基础
c++·qt·设计模式·设计规范
qq_3928079516 小时前
C++ 多线程编程
开发语言·c++