سؤال

لنفترض أن لدي وظيفة مجانية تسمى InitFoo. وبعد أرغب في حماية هذه الوظيفة من تسمى عدة مرات عن طريق الصدفة. بدون الكثير من التفكير كتبت ما يلي:

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

    //Actual code goes here.
}

هذا يبدو وكأنه ثلث كبيرة، رغم ذلك. InitFoo هل ليس بحاجة إلى الحفاظ على أي معلومات دولة أخرى. هل يمكن لشخص ما أن يقترح وسيلة لإنجاز نفس الهدف دون القبح؟

وحدات الماكرو لا تحسب، بالطبع.

هل كانت مفيدة؟

المحلول

يمكنك أن تفعل ذلك مع بعض القبح المختلفة:

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

void Foo()
{
    static InitFoo i;
}

أنت لا تزال تستخدم static, ، ولكن الآن لا تحتاج إلى القيام بفحص العلم الخاص بك - static يضع بالفعل في العلم والتحقق من ذلك، لذلك يبني فقط i بمجرد.

نصائح أخرى

حسنا، يتم استدعاء المنشئ تلقائيا تلقائيا مرة واحدة. إذا قمت بإنشاء مثيل واحد من هذه الفئة:

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

ثم //do stuff سوف تنفذ مرة واحدة فقط. الطريقة الوحيدة لتنفيذها مرتين هي إنشاء مثيل آخر من الفصل.

يمكنك منع هذا باستخدام singleton.. وبعد في الواقع، //do stuff يمكن أن يسمى فقط مرة واحدة.

أود حماية هذه الوظيفة من تسمى عدة مرات عن طريق الصدفة

بالنسبة لي، هذا يبدو وكأنه مشكلة ستظهر فقط أثناء التصحيح. إذا كان هذا هو الحال، فسأقوم ببساطة بما يلي:

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

    ...
}

الغرض من هذه البثرات بالذات يتم تمييزها بسهولة فقط من خلال النظر إليها، وسوف يتسبب في فشل تأكيد لطيف ومهاز للغاية إذا كان مبرمج يرتكب خطأ في الاتصال InitFoo أكثر من مرة. وسوف تختفي أيضا في قانون الإنتاج تماما. (متي NDEBUG ويعرف).

تعديل: ملاحظة سريعة عن الدافع:
إن استدعاء وظيفة Init أكثر من مرة ربما يكون خطأ كبيرا. إذا كان المستخدم النهائي لهذه الوظيفة قد أطلق عليه عن طريق الخطأ مرتين، فإن تجاهل بهدوء هذا الخطأ ربما ليس هو الطريق للذهاب. إذا كنت لا تذهب assert() الطريق، أود أن أوصى على الأقل إغراق رسالة stdout أو stderr.

هذا هو بالضبط كيف أفعل ذلك. يمكنك استخدام بعض خلط مؤشر الوظائف إذا كنت تريد بديلا:

static void InitFoo_impl()
{
    // Do stuff.

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

void (*InitFoo)() = &InitFoo_impl;

هل تحتاج أيضا إلى أن تكون آمنة متعددة الخيط؟ ابحث في نمط Singleton مع قفل فحص مزدوج (وهو أمر مثير للدهشة سهلة الحصول على خطأ).

إذا كنت لا تريد فئة كاملة لهذا، طريقة أخرى بسيطة هي:

في .cpp (لا تعلن initblah في .h)

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

لا أحد يستطيع الاتصال به خارج هذا .cpp، ويتم استدعاؤه. بالتأكيد، يمكن لشخص ما يسميه في هذا .cpp - يعتمد على مقدار تهتم به من المستحيل مقابل غير مريح وتوثيق.

إذا كنت تهتم بالطلب أو القيام بذلك في وقت محدد، فمن المحتمل أن يكون Singleton بالنسبة لك.

أنا أقوم بالضبط في كل وقت مع المواقف التي تحتاج إلى وجود فئة واحدة فقط - ولكن غير جديرة بالجدل بالطبع، يفترض أنك لا تقلق بشأن القضايا المتعلقة بالموضوع. عادة ما تكون بادئة الاسم المتغير مع "S_" لتتيح أنه من الواضح أنه متغير ثابت.

هممم ... إذا كنت لا تعترض على استخدام تعزيز, ، إلقاء نظرة على دفعة :: 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!
}

ليس هذا أنا متحمس للغاية حوله، ولكن هذه مجرد طريقة أخرى: كائن وظيفة.

#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();
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top