千载难逢的

时间:2014-02-17 13:27:48

标签: c++ design-patterns

有没有办法强制执行成员函数的执行次数?为了解释,让我说我有

class foo {
public:
    void Once();
};

我希望 foo的每个实例都只调用一次成员函数foo :: Once()。如果已经进行了呼叫,则禁止再次呼叫,如果没有呼叫,则在销毁之前呼叫。

  • 是否有与此相关的习语或设计模式?
  • foo::was_called_flag成员(我会设置为0或1)的硬编码解决方案是可接受的还是存在漏洞需要考虑?
  • 是否有一个可以很好地扩展到任意数量的执行的解决方案

4 个答案:

答案 0 :(得分:2)

Afaik,没有预先构建的解决方案。但是,自己编写它很简单。以下是您需要考虑的要点:

  1. 标志/计数器没问题。但是,如果您具有对象的并发访问权限,则应使用类型为std::atomic<>的标记/计数器。不要使用锁,这是完全矫枉过正的。

  2. 如您所述,您需要检查函数中的标志/计数器。

  3. 您还需要在析构函数中插入对函数的调用。与所有析构函数一样,它应该是虚拟的。但是,请注意,如果对象属于您的类的某个子类,则所有子类析构函数将在析构函数有机会调用Once()之前运行。因此,您可能在不正常的对象上调用Once()

  4. 如果必须确保在可能的子类被破坏之前运行Once(),则必须将析构函数的调用移动到设置另一个标志的finalize()方法,并断言你的析构函数中的标志。

答案 1 :(得分:1)

  

是否有与此相关的习语或设计模式?

class Foo {
    bool once_called;
  public:
    void once()
    {
        if (once_called)
            throw TwiceError();
        once_called = true;
    }
};
  

是否存在需要考虑的漏洞?

有一种潜在的竞争条件,两个线程试图同时在同一个对象上调用once

  

是否存在可以很好地扩展到任意数量的执行的解决方案

一个计数器,最初是n。递减成员函数中的计数器,并在计数器达到零时使调用失败。

答案 2 :(得分:1)

嗯,反制方法是我能想到的最佳方式。您应该确保在多线程场景中正确缩放它,即。保护计数器免受数据争用:

#include<mutex>

class foo {
public:
    foo() : counter (0) {}

    const static int CALL_LIMIT = 1;

    void Once()
    {
        mtx.lock();
        counter++;
        if (counter>CALL_LIMIT)
        {
            mtx.unlock();
            return; /* or throw an error */
        } 
        mtx.unlock();
        /* Rest of the code */
    }

private:
    static std::mutex mtx;
    int counter;

};

当然你也应该考虑在析构函数中调用Once()。

答案 3 :(得分:0)

我假设你总是可以创建一个天真的解决方案,比如(更多的算法而不是C ++代码):

class foo {
   static std::vector<foo*> callers;

   void Once()
   {
       if(std::find(callers.begin(), callers.end(), this)!=callers.end())
       {
           // do stuff ...
           callers.push_back(this);
       }   
   }
显然,如果您需要能够控制允许呼叫者调用和修改代码的次数,您可以拥有std::map<foo*, int>

解决“没有调用,然后在破坏之前调用”(不确定我是否理解正确)你只需从析构函数中调用Once(),这样就可以解决问题。

当然,另一个问题出现了:如果操作系统认为在与已经删除的元素相同的位置创建元素是明智的决定,那么...为了避免这种情况,在析构函数结束时,或者在构造函数的开头,你应该从静态向量/ map中删除this