Способ деструктора C ++ пропустить работу, когда выбрасывается конкретное исключение? - PullRequest
1 голос
/ 24 июля 2010

У меня есть объект в стеке, для которого я хотел бы, чтобы его деструктор пропустил некоторую работу, когда вызывается деструктор, потому что стек разматывается из-за определенного исключения, выбрасываемого через область объекта в стеке.1001 *

Теперь я могу добавить блок try catch внутри области действия элемента стека, перехватить рассматриваемое исключение и уведомить объект стека, чтобы он не выполнял пропускаемую работу, а затем повторно выдать исключение следующим образом:

RAII_Class pending;

try {
  doSomeWorkThatMayThrowException();
} catch (exceptionToSkipPendingDtor &err) {
  pending.notifySkipResourceRelease();
  throw;
}

Однако я надеюсь, что есть более элегантный способ сделать это.Например, представьте:

RAII_Class::~RAII_Class {
  if (detectExceptionToSkipPendingDtorBeingThrown()) {
    return;
  }
  releaseResource();
}

Ответы [ 7 ]

8 голосов
/ 24 июля 2010

Вы можете сделать это почти с std::uncaught_exception(), но не совсем.

Херб Саттер объясняет "почти" лучше, чем я: http://www.gotw.ca/gotw/047.htm

В некоторых случаях std::uncaught_exception() возвращает true при вызове из деструктора, но рассматриваемый объект на самом деле не уничтожается процессом разматывания стека.

Возможно, вам лучше без RAII, потому что он не соответствует вашему варианту использования.RAII означает всегда убирать;исключение или нет.

То, что вы хотите, намного проще: освобождайте ресурс, только если исключение не является throw, что является простой последовательностью функций.

explicitAllocateResource();
doSomeWorkThatMayThrowException();
explicitReleaseResource(); // skipped if an exception is thrown
                           // by the previous function.
5 голосов
/ 24 июля 2010

Я бы сделал это наоборот - явно сказал бы, чтобы он выполнял свою работу, если не было выдано исключения:

RAII_Class pending;

doSomeWorkThatMayThrowException();

pending.commit(); // do or prepare actual work
3 голосов
/ 24 июля 2010

Это, кажется, обходит основную причину использования RAII. Смысл RAII в том, что если в середине вашего кода происходит исключение, вы все равно можете освободить ресурсы / быть уничтожены должным образом.

Если это не та семантика, которую вы хотите, то не используйте RAII.

Так что вместо:

void myFunction() {
    WrapperClass wc(acquireResource());

    // code that may throw
}

Просто сделай:

void myFunction() {
    Resource r = acquireResource();

    // code that may throw

    freeResource(r);
}

Если код посередине выбрасывает, ресурс не будет освобожден. Это то, что вам нужно, вместо сохранения RAII (и сохранения имени), но не реализации семантики RAII.

0 голосов
/ 24 июля 2010

Несмотря на то, что в лучшем случае это было бы препятствием, если вы владеете кодом интересующего вас класса исключений, вы можете добавить к этому классу статический член данных (bool), который будет установлен в "true" конструктор для объектов этого класса и false в деструкторе (может потребоваться быть int, который вы вместо этого увеличиваете / уменьшаете). Затем в деструкторе вашего класса RAII вы можете проверить std :: uncaught_exception () и, если true, запросить член статических данных в вашем классе исключений. Если вы вернете true (или> 0), у вас есть одно из этих исключений - иначе вы его проигнорируете.

Не очень элегантно, но, вероятно, это поможет (если у вас нет нескольких потоков).

0 голосов
/ 24 июля 2010

Я нашел этот сайт с интересной дискуссией о std :: uncaught_exception () и альтернативным решением вашего вопроса, которое мне кажется гораздо более элегантным и правильным:

http://www.gotw.ca/gotw/047.htm

//  Alternative right solution
//
T::Close() {
  // ... code that could throw ...
}

T::~T() /* throw() */ {
  try {
    Close();
  } catch( ... ) {
  }
}

Таким образом, ваш деструктор делает только одно, и вы защищены от создания исключения во время исключения (которое, я полагаю, является проблемой, которую вы пытаетесь решить).

0 голосов
/ 24 июля 2010

Вы можете обойтись без try-catch:

RAII_Class pending;
doSomeWorkThatMayThrowException();  // intentional: don't release if throw
pending.releaseResource();

В качестве альтернативы, вы можете попробовать немного больше с RAII:

struct RAII_Class {
    template<class Op>
    void execute(Op op) {
        op();
        releaseResources();
    }

private:
    void releaseResources() { /* ... */ }
};

int main(int argc, char* argv[])
{
    RAII_Class().execute(doSomeWorkThatMayThrowException);
    return 0;
}
0 голосов
/ 24 июля 2010

Выглядит как bool std :: uncaught_exception (); делает трюк, если вы хотите иметь такое поведение для каждого исключения, а не только для специальных!

...