限制一个类的实例数

时间:2018-08-18 06:16:21

标签: c++ c++11 memory

我想限制可以创建一个类的实例数。

我有以下代码:

class A {
    static int cnt;
    int x;
    public:
    A() {
        cout<<"ctor called\n";
    }
    void* operator new(size_t size_in) {
        if(cnt<=10) {
            void *p=malloc(size_in);

            if(p==NULL) {
                throw bad_alloc();
            } else {
                cout<<"Memory allocation successful\n"; 
                ++cnt;
                return p;
            }
        } else {
            throw bad_alloc();
        }
    }
    ~A() {
        cout<<"Cleaning up the mess\n";
    }
};

int A::cnt=0;
int main() {
    A *a[20];
    for(int i=0;i<20;++i) {
        try {
            a[i]=new A();
        } catch (bad_alloc &e) {
            cout<<"Error in allocating memory\n";
        }
    }
    try {
        A b;
    } catch (bad_alloc &e) {
        cout<<"Error in allocating memory on stack\n";
    }
    return 0;
}

使用静态计数器并重载new运算符,我可以限制可以在Heap上创建的对象数量。我也想限制在Stack上创建的实例的数量。一种方法是使构造函数私有,并提供一个公共API,该API首先检查计数器,然后相应地返回。 还有其他方法吗?

4 个答案:

答案 0 :(得分:5)

  

还有其他方法吗?

您可能只是增加并检查构造函数中的计数器,如果您抛出异常,则该对象将被破坏。此外,您不必区分堆栈和堆。

答案 1 :(得分:2)

最好的方法是创建帮助器模板类并使用构造函数和析构函数对对象进行计数:

class instance_limit_reached : public std::logic_error
{
public:
    using logic_error::logic_error;
};

template<typename T, int MaxInst>
class LimitInstances
{
    static std::atomic<int> instanceCount;

    void onNewInstance() {
        chekcTheLimit();
        ++instanceCount;
    }

    void chekcTheLimit() {
        if (instanceCount >= MaxInst)
            throw instance_limit_reached(std::string("Limit reached for ") + typeid(T).name());
    }

public:
    ~LimitInstances() {
        --instanceCount;
    }
    LimitInstances() {
        onNewInstance();
    }
    LimitInstances(const LimitInstances<T, MaxInst> &) {
        onNewInstance();
    }
    LimitInstances(LimitInstances<T, MaxInst> &&) {
        onNewInstance();
    }
};

实时example with field useexample with CRTP

现在有一个重要的问题,当对象移动时,您将其视为新实例(我的示例)还是旧实例(我的代码需要调整)?

答案 2 :(得分:1)

有趣的是,这就是我的方法:CRTP设计可与线程安全代码一起重用:

template<class ToLimit,size_t MaxInstances>
class InstanceLimiter{
   static inline std::atomic<int> instances=0;
   private:
   static increase_count(){
      //memory order relaxed is sufficient because there is
      //only one modification order for each atomic objects.
      int actual=instances.load(std::memory_order_relaxed);
      do{
        if (actual>=MaxInstances) throw some_error{};
      } while (instances.compare_exchange_weak(actual,actual+1,
                 std::memory_order_relaxed,std::memory_order_relaxed));
      }
    protected:
    //Provide definition for default constructor, copy constructor
    // and copy assignment operator so that defaulted derived special
    // member function behave as expected.
    InstanceLimiter(){increase_count();}

    InstanceLimiter(const InstanceLimiter&){increase_count();}

    InstanceLimiter& operator=(const InstanceLimiter&){
      increase_count();
      return *this;
      }

    ~InstanceLimiter(){
       instances.fetch_add(-1,std::memory_order_relaxed);
       }
    };

class A: InstanceLimiter<A,10> {
  int x;
  public:
  A() {
    //InstanceLimiter default constructor implicitly called
    cout<<"ctor called\n";
    }
  A(int x)
    //InstanceLimiter default constructor implicitly called here
    :x{x}{}
  //Implicitly declarer move/copy constructor/assignement implicitly calls
  // the copy constructor/assignment of InstanceLimiter
  ~A() {
      cout<<"Cleaning up the mess\n";
      //Default destructor of InstanceLimiter implicitly called here.
    }
  };

最后但并非最不重要的一点:如果您打算在实际代码中使用它,请考虑将您的A类设置为noexcept默认值,并通过为其提供一个不计入实例的默认状态来使其可构造。

答案 3 :(得分:1)

  

我也想限制在Stack上创建的实例数

如果您想对Heap和Stack对象设置不同的限制,在我看来,更干净的方法是使用带有make函数的朋友make函数(一个用于堆对象,一个用于堆栈对象)的私有构造函数,以及在make函数内部的计数器

我的意思是...您可以按如下方式写A

class A
 {
   private:
      int x;

      A (int x0 = 0)
       { std::cout << "ctor called" << std::endl; }

   public:
      ~A()
       { std::cout << "cleaning up the mess" << std::endl; }

      friend A * makeAinHeap (int);
      friend A makeAinStack (int);
 };

堆内函数很简单

A * makeAinHeap (int x)
 {
   constexpr auto maxAH { 3u };

   static auto ah { 0u };

   if ( ++ah > maxAH )
      throw std::runtime_error("no more A in Heap");

   return new A{x};
 }

和类似的堆叠功能是

A makeAinStack (int x)
 {
   constexpr auto maxAS { 2u };

   static auto as { 0u };

   if ( ++as > maxAS )
      throw std::runtime_error("no more A in Stack");

   return A{x};
 }

您可以使用以下main()

进行全部检查
int main ()
 {
   auto p1 { makeAinHeap(0) }; // OK
   auto p2 { makeAinHeap(0) }; // OK
   auto p3 { makeAinHeap(0) }; // OK
   //auto p4 { makeAinHeap(0) }; // throw an exception

   auto s1 { makeAinStack(0) }; // OK
   auto s2 { makeAinStack(0) }; // OK
   //auto s3 { makeAinStack(0) }; // throw an exception

   delete p1;
   delete p2;
   delete p3;
 }