2009-10-14 7 views
4

Я прочитал статью о защитных планах (Generic: Change the Way You Write Exception-Safe Code — Forever) в 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? У кого-нибудь есть более умный подход?

ответ

6

Кажется, вы не цените RAII за то, что это. Эти ограждения для области хорошо подходят для локальных («объемных») вещей, но вы должны стараться избегать их в пользу того, что действительно должно делать RAII: инкапсуляция ресурса в объект. Тип FILE * на самом деле просто не хорош в этом.

Вот альтернатива:

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(...)); 
    } 
} 

Edit: Так как мне нравится подвижных типов в C++ 0x, и вы проявили к нему интерес: вот как вы могли использовать unique_ptr в сочетании с FILE * без каких-либо ссылок подсчета накладных расходов:

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

(непроверенных)

быть уверены, чтобы проверить Dave's blog on efficient movable value types

+0

Да, вы правы, что RAII предпочтительнее для деструктора ресурса (например, дескриптора файла). Я использую только защитные оболочки для вещей, которые было бы неудобно представлять в качестве «ресурса», например. отчет (OperationStart); ON_BLOCK_EXIT (отчет, OperationEnd); doSomething(); – Ilya

+0

К сожалению, у меня еще нет TR1 в моем компиляторе, поэтому я не могу использовать shared_ptr. Тем не менее, вектор/очередь auto_ptr может просто работать (предполагая, что я бы купил выделение границ области и нажал их в вектор/очередь). Я заинтригован, узнав, что некоторые классы C++ станут «подвижными» (не знали об этой терминологии). Это делается с пересчетами? Может быть, я должен сделать так, чтобы охранники были «подвижными»? – Ilya

+4

Вектор/очередь auto_ptr <...> явно не очень хорошая идея, так как auto_ptrs не копируются! –

0

Ха, оказывается, объем защитного DDJ является "подвижный", а не в C++ 0x смысл, но в том же смысле, что auto_ptr является подвижным: во время копирования ctor новый охранник «увольняет» старого охранника (например, копия auto_ptr ctor вызывает старую auto_ptr :: release).

Так что я могу просто держать 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)); 
    // ... 
} 

Кстати, спасибо за ответ выше. Это было информативным и образовательным для меня по-разному.

+0

Да. Его конструктор копирования «перемещается», что делает его также небезопасным как auto_ptr. Для версии C++ 0xified проверьте мой пост в блоге: http://pizer.wordpress.com/2008/11/22/scope-guards-revisited-c0x-style/ – sellibitze

+0

Просто не делайте этого! Это не правильно. Это плохо. Это не сработает. Объект защиты, который они показывают в этой статье DDJ, так же плох, как auto_ptr. Он перемещается по копии. Это не так, как должен выглядеть тип ценности! – sellibitze

+0

Кроме того, ScopeGuard ist typedef для «ScopeGuardBase const &», если я правильно помню. Просто используйте shared_ptr для обработки указателей FILE уже! ;-) – sellibitze

Смежные вопросы