Динамически создаваемые охранники области
-
21-09-2019 - |
Вопрос
Я прочитал статью о защитных устройствах (Общий:Измените способ написания безопасного к исключениям кода — навсегда) в 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:инкапсуляция ресурса в объект.Тип 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(...) );
}
}
Редактировать:Поскольку мне очень нравятся подвижные типы в 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() );
}
(непроверено)
Обязательно проверьте Блог Дэйва об эффективных типах перемещаемых значений
Другие советы
Да, оказывается, защита области DDJ «подвижна», не в смысле C++0x, а в том же смысле, в котором auto_ptr является подвижным:во время копирования новый охранник «увольняет» старого охранника (например, копирующий фактор auto_ptr вызывает старого 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));
// ...
}
Кстати, спасибо за ответ выше.Для меня это было познавательно и познавательно по-разному.