C ++ Безопасная обработка исключений - PullRequest
7 голосов
/ 13 мая 2011

Давайте разберемся с фрагментом кода (fstream - просто пример, мы могли бы говорить о динамическом распределении памяти ...):


fstream f;
try {
f.open("xxx");
    ...
f.close();
} catch (...) {
    ...
}

Когда что-то идет не так, я хотел бы закрыть () файл (освободить память или что-то еще), но я не знаю состояния f. В конце концов, исключение может исходить от f.open (). Я не думаю, что было бы безопасно вызывать f.close () в предложении catch, так как я больше не могу поверить в f.

f также может быть указателем на динамически размещенный массив, который я хотел бы удалить [], но кто знает, куда он указывает после того, как было сгенерировано исключение ...

Это может быть не очень часто, но что мне делать, если я абсолютно не могу нанести дополнительный урон?

Я могу думать о немедленном прерывании ().

Спасибо.

Ответы [ 6 ]

11 голосов
/ 13 мая 2011

Вы должны использовать RAII или широко известный здесь как SBRM (Scope Based Resource Management) :)

10 голосов
/ 13 мая 2011

fstream деструкторы зовут close для вас. При возникновении исключения файл закрывается автоматически.

Для управления памятью вы можете использовать умные указатели.

Для управления мьютексами или более общими блокировками большинство библиотек предоставляют вам класс, деструктор которого разблокирует мьютекс для вас.

Никогда не пишите код в виде:

acquire a resource
do stuff which can throw
release a resource

Вместо этого используйте объекты, чьи деструкторы высвобождают ресурс для вас.

4 голосов
/ 13 мая 2011

Деструктор fstream будет вызывать close () для вас, поэтому вам не нужно закрывать его самостоятельно (если вы не хотите видеть код возврата close ()).

2 голосов
/ 13 мая 2011

В вашем примере вы можете переместить объявление f в блок try, чтобы убедиться, что оно уничтожает себя;деструктор знает состояние объекта.

В качестве другого примера, с выделением памяти вы можете инициализировать указатель на 0 до того, как память фактически будет выделена, а затем снова сбросить его на ноль, когда вы освободите выделенную память.Это позволит вам проверить, была ли выделена память, чтобы избежать освобождения памяти, которая больше не принадлежит вам.Пример:

char *name = 0;
try {
    //  perform some operations which may throw...

    //  now allocate
    name = new char[20];

    //  more error prone operations here
}
catch(...){
    if(name != 0){
        delete[] name;
        name = 0;
    }
}

Опять же, вы можете использовать RAII и здесь.Пример:

class MyCharArray {
    char *str;
public:
    MyCharArray(size_t size){
        str = new char[size];
    }

    ~MyCharArray(){
       delete[] str;
   }
};

int main(){
    try {
        //  perform some operations which may throw...

        MyCharArray name(20);

        //  more error prone operations here
    }
    catch(...){
        //  no additional error handling required here
    }
    return 0;
}

Обратите внимание, что RAII считается лучшим, потому что вы пишете код очистки только один раз - в деструкторе - а не после каждого try блока.

1 голос
/ 13 мая 2011

Включить исключения для объекта fstream и обработать исключение в точке, где это возможно:

void foo()
{
  std::fstream f( "lala.txt" );
  f.exceptions( std::fstream::failbit | std::fstream::badbit )
  // do something
}
0 голосов
/ 13 мая 2011

Я бы не стал использовать повышение для того, чтобы обманывать RAII:

#include <boost/smart_ptr.hpp>
#include <fstream>

using namespace std;

static void do_close(fstream* f)
{
    try 
    { 
        f->close(); 
        delete f;
    } catch(...) { /* log the error? */ }
}

int main()
{
    boost::shared_ptr<fstream> f(new fstream(), &do_close);
    try 
    {
        f->open("xxx");
        f->close();
    } catch (...)
    {
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...