2

假设我有一个名为InitFoo. 我想保护这个函数不被意外调用多次。没有多想,我写了以下内容:

void InitFoo()
{
    {
        static bool flag = false;
        if(flag) return;
        flag = true;
    }

    //Actual code goes here.
}

不过,这看起来像一个大疣。InitFoo不需要保留任何其他状态信息。有人可以建议一种方法来实现相同的目标而不会丑陋吗?

当然,宏不算数。

4

8 回答 8

10

你可以用一些不同的丑陋来做到这一点:

struct InitFoo
{
     InitFoo()
     {
         // one-time code goes here
     }
};

void Foo()
{
    static InitFoo i;
}

您仍在使用static,但现在您不需要进行自己的标志检查 -static已经放入标志并对其进行检查,因此它只构造i一次。

于 2009-07-20T22:11:54.720 回答
3

好吧,构造函数只会自动调用一次。如果您创建此类的单个实例:

class Foo
{
public:
    Foo(void)
    {
        // do stuff
    }
}

然后//do stuff只会执行一次。执行它两次的唯一方法是创建该类的另一个实例。

您可以通过使用Singleton来防止这种情况。实际上,//do stuff只能调用一次。

于 2009-07-20T22:09:49.327 回答
1

我就是这样做的。如果你想要一个替代方案,你可以使用一些函数指针改组:

static void InitFoo_impl()
{
    // Do stuff.

    // Next time InitFoo is called, call abort() instead.
    InitFoo = &abort;
}

void (*InitFoo)() = &InitFoo_impl;
于 2009-07-20T22:14:42.423 回答
1

我想保护这个函数不被意外调用多次

对我来说,这听起来像是一个只会在调试期间出现的问题。如果是这种情况,我只需执行以下操作:

void InitFoo()
{
    #ifndef NDEBUG
       static bool onlyCalledOnce = TRUE;
       assert(onlyCalledOnce);
       onlyCalledOnce = FALSE;
    #endif

    ...
}

这个特殊缺陷的目的很容易通过查看它来辨别,如果程序员犯了InitFoo多次调用的错误,它将导致一个漂亮的、大的、华丽的断言失败。它也将在生产代码中完全消失。(何时NDEBUG定义)。

编辑:关于动机的快速说明:
多次调用 init 函数可能是一个大错误。如果这个函数的最终用户错误地调用了它两次,那么默默地忽略这个错误可能不是要走的路。如果您不走这assert()条路线,我建议至少将消息转储到stdoutor stderr

于 2009-07-20T22:18:53.740 回答
0

你还需要它是多线程安全的吗?查看带有双重检查锁定的单例模式(这很容易出错)。

如果您不想要整个班级,另一种简单的方法是:

在 .cpp 中(不要在 .h 中声明 InitBlah)

 // don't call this -- called by blahInited initialization
static bool InitBlah() 
{
   // init stuff here
   return true;
}
bool blahInited = InitBlah();

没有人可以在这个 .cpp 之外调用它,它会被调用。当然,有人可以在这个 .cpp 中调用它——这取决于你有多在意它是不可能的,还是不方便和记录在案的。

如果您关心订单或在特定时间执行订单,那么 Singleton 可能适合您。

于 2009-07-20T22:18:34.787 回答
0

在需要一次性但不值得制作一个全班的情况下,我总是这样做。当然,它假设您不担心与线程相关的问题。我通常在变量名前加上“s_”,以明确它是一个静态变量。

于 2009-07-20T22:35:03.020 回答
0

嗯...如果您不反对使用Boost,请查看boost::call_once

namespace { boost::once_flag foo_init_flag = BOOST_ONCE_INIT; }

void InitFoo() {
    // do stuff here
}

void FooCaller() {
    boost::call_once(&foo_init_flag, InitFoo);
    // InitFoo has been called exactly once!
}

void AnotherFooCaller() {
    boost::call_once(&foo_init_flag, InitFoo);
    // InitFoo has been called exactly once!
}
于 2009-07-21T03:26:03.173 回答
0

并不是说我对此很兴奋,但这只是另一种方式:函数对象。

#import <iostream>

class CallOnce {
private:
    bool called;
public:
    CallOnce() {
        called = false;
    }
    void operator()(void) {
        if (called) {
            std::cout << "too many times, pal" <<std::endl;
            return;
        }
        std::cout << "I was called!" << std::endl;
        called = true;
    }

};

int main(void) {
    CallOnce call;

    call();
    call();
}
于 2009-07-21T03:35:05.573 回答