【问题标题】:limit the number of instance of a class限制类的实例数
【发布时间】:2018-08-18 06:16:20
【问题描述】:

我想限制一个类的实例数量。

我有以下代码:

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 首先检查计数器,然后相应地返回。 有没有其他方法可以做到这一点?

【问题讨论】:

  • 请注意,您的解决方案在多线程程序中效果不佳。
  • 是的,我同意。至于现在我只关注单线程环境
  • 你的限制是创建的对象总数,还是一次的对象总数?每个解决方案略有不同。
  • 如果你不讨厌 Singleton,你可以根据预设的实例数量来调整它。
  • @john 创建的对象总数,而不是一次。一次性限制对象的数量..我想我应该检查我要求的大小...正确如果我错了,我。

标签: c++ c++11 memory


【解决方案1】:

还有其他方法吗?

你可以在构造函数中增加并检查计数器,如果你抛出异常,对象将被销毁。此外,您不必区分堆栈和堆。

【讨论】:

  • 可能要注意增加/检查计数器需要发生在所有构造函数中。这意味着要注意确保所有构造函数都进行检查 - 编译器生成的构造函数不会。
  • 嗯,我想到了。抛出异常会强制对象被销毁。但关键是,它会首先被创建,然后因为抛出异常而被销毁。我希望它不应该被创建甚至放在首位。就像它发生在堆中一样。
  • @aalisha 您认为在堆栈上创建对象的成本是多少?分配内存仅仅意味着增加栈指针,全部开销在构造函数中,这将是快捷方式。
  • 是的,我知道 ESP 会先增后减。关键是,如果 cnt >10,则不会调用堆 sbrk(假设 malloc 的简单实现)。但在堆栈的情况下我们将构造(而不是分配)然后销毁。
  • @aalisha:建筑有什么问题?您的构造函数所做的只是检查计数器值并最终抛出,这是您无论如何都会做的一件事。我完全没有看到你的问题。对我来说,这更像是过早的优化。你衡量过影响吗?
【解决方案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

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

【讨论】:

    【解决方案3】:

    为了好玩,我会这样做: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 默认并通过为其提供不计为实例的默认状态来移动可构造。

    【讨论】:

      【解决方案4】:

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

      如果你想对 Heap 和 Stack 对象有不同的限制,在我看来,更简洁的方法是使用带有友元 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};
       }
      

      类似的make-in-stack函数是

      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;
       }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-08
        • 1970-01-01
        相关资源
        最近更新 更多