سؤال

لقد قرأت المقالة حول حراس النطاق (عام: غير طريقة تكتب رمزًا آمنًا للاستثناء-إلى الأبد) في DDJ وأنا أفهم استخدامهم المشترك.

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

{
    FILE* topSecret = fopen("cia.txt");
    ON_BLOCK_EXIT(std::fclose, topSecret);
    ... use topSecret ...
} // topSecret automagically closed

ولكن ماذا لو كنت أرغب في جدولة عمليات التنظيف في وقت التشغيل ، على سبيل المثال عندما يكون لدي حلقة:

{
   vector<FILE*> topSecretFiles;
   for (int i=0; i<numberOfFiles; ++i)
   {
      char filename[256];
      sprintf(filename, "cia%d.txt", i);
      FILE* topSecret = fopen(filename);
      topSecretFiles.push_back(topSecret);
      ON_BLOCK_EXIT(std::fclose, topSecret); // no good
   }
}

من الواضح أن المثال أعلاه لن يعمل منذ ذلك الحين topSecret سيتم إغلاقه مع ل الكائن. أرغب في الحصول على نمط حراسة النطاق حيث يمكنني بسهولة وضع عمليات تنظيف بسهولة والتي حددها اللازمة في وقت التشغيل. هل هناك شيء مثل هذا متاح؟

لا يمكنني دفع كائنات حارس النطاق إلى قائمة انتظار قياسية ، حيث يتم رفض الكائن الأصلي (الكائن الذي أدفعه) في هذه العملية. ماذا عن دفع حراس المكدس الملقح كومة واستخدام قائمة انتظار يحذف أعضائها على DTOR؟ هل لدى أي شخص مقاربة أكثر ذكاءً؟

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

المحلول

يبدو أنك لا تقدر Raii على ما هو عليه. هؤلاء حراس النطاقون لطيفون في بعض الأحيان للأشياء المحلية ("النطاق") ولكن يجب أن تحاول تجنبها لصالح ما يفترض أن يفعله RAII حقًا: تغليف مورد في كائن. ملف النوع* حقًا ليس جيدًا في ذلك.

هذا بديل:

void foo() {
    typedef std::tr1::shared_ptr<FILE> file_sptr;
    vector<file_sptr> bar;
    for (...) {
        file_sptr fsp ( std::fopen(...), std::fclose );
        bar.push_back(fsp);
    }
}

أو:

void foo() {
    typedef std::tr1::shared_ptr<std::fstream> stream_sptr;
    vector<stream_sptr> bar;
    for (...) {
        file_sptr fsp ( new std::fstream(...) );
        bar.push_back(fsp);
    }
}

أو في "C ++ 0x" (معيار C ++ القادم):

void foo() {
    vector<std::fstream> bar;
    for (...) {
        // streams will become "movable"
        bar.push_back( std::fstream(...) );
    }
}

تحرير: بما أنني أحب الأنواع المنقولة في C ++ 0x كثيرًا وأظهرت اهتمامًا به: إليك كيف يمكنك استخدام فريدة من نوعها في تركيبة مع ملف* بدون أي نفقات عامة على ترديد:

struct file_closer {
    void operator()(FILE* f) const { if (f) std::fclose(f); }
};

typedef std::unique_ptr<FILE,file_closer> file_handle;

file_handle source() {
    file_handle fh ( std::fopen(...) );
    return fh;
}

int sink(file_handle fh) {
    return std::fgetc( fh.get() );
}

int main() {
    return sink( source() );
}

(لم يخبر)

تأكد من تسجيل الخروج مدونة ديف على أنواع القيمة المنقولة الفعالة

نصائح أخرى

هاه ، تبين أن حارس نطاق DDJ هو "متحرك" ، ليس بالمعنى C ++ 0x ، ولكن بنفس المعنى أن auto_ptr متحرك: أثناء النسخ ctor ، يرفض الحارس الجديد "الحارس القديم (مثل Auto_ptr's نسخ CTOR يدعو إلى القديم Auto_ptr :: الإصدار).

لذلك يمكنني ببساطة الاحتفاظ queue<ScopeGuard> وسوف تعمل:

queue<ScopeGuard> scopeGuards;

// ...

for (...)
{
   // the temporary scopeguard is being neutralized when copied into the queue,
   // so it won't cause a double call of cleanupFunc
   scopeGuards.push_back(MakeScopeGuard(cleanupFunc, arg1));
   // ...
}

بالمناسبة ، شكرا لك على الإجابة أعلاه. كانت مفيدة وتعليمية بالنسبة لي بطرق مختلفة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top