Код очистки в деструкторе исключения C++
Можем ли мы использовать деструктор исключения как место для размещения некоторого кода очистки?
Таким образом, мы можем позволить клиенту контролировать этап завершения в отличие от RAII. Это хороший или плохой дизайн? Это правильное решение в контексте ООП и C++?
В настоящее время я работаю над асинхронной процедурой, которая сама запускает несколько асинхронных задач. Шаблон выглядит следующим образом:
struct IAsyncResult
{
...
virtual void EndCall() const;
}
typedef std::shared_ptr<IAsyncResult> IAsyncResultPtr;
struct IAsyncTask
{
virtual IAsyncResultPtr BeginTask() = 0;
virtual void EndTask(IAsyncResultPtr async) const = 0;
}
class CompositeTask : public IAsyncTask
{
…
}
К сожалению, я не могу гарантировать, что метод BeginTask каждой подзадачи не завершится с ошибкой. Таким образом, вполне возможно, что подзадачи N-1 будут успешно запущены, а N-й сбой.
В общем, важно убедиться, что никакие фоновые задачи не выполняются до завершения клиентского кода. Но иногда клиенту все равно, если некоторые задачи не выполняются.
Таким образом, мое текущее решение включает в себя пользовательское исключение, которое выдается из метода BeginAsync CompositeTask в случае, если не удалось запустить одну задачу. Это позволяет клиенту контролировать этап очистки:
class composite_async_exception : public std::exception
{
std::vector<IAsyncResultPtr> successfully_started_tasks;
mutable bool manage_cleanup;
public:
composite_async_exception(std::vector<IAsyncResultPtr> const& _successfully_started_tasks)
: successfully_started_tasks(_successfully_started_tasks)
, manage_cleanup(true)
{
}
virtual ~composite_async_exception() throw()
{
if(!manage_cleanup)
return;
for( auto task = successfully_started_tasks.begin(); task != successfully_started_tasks.end(); ++task)
{
task->CancelTask();
}
}
void Giveup() const throw()
{
manage_cleanup = false;
}
};
И клиент использует код как показано:
try
{
compositeTask.BeginAsync();
}
catch(composite_async_exception const& ex)
{
//prevent the exception to cancel tasks
ex.Giveup();
// some handling
}
Существуют ли лучшие практики для решения такой ситуации?
1 ответ
- Исключение может быть скопировано, деструктор будет вызываться несколько раз. В вашем случае это не проблема.
- Механизм обработки исключений может остановить ваши задачи, уничтожив объект временного исключения, прерывающий ваши задачи в точке выброса, а не при обработке.
Чтобы убедиться в этом, нужно прочитать стандарт, что мне лень делать.