Более сложная очистка в C ++

Я хорошо осведомлен о шаблоне RAII, std::unique_ptr и других «умных указателях» в C ++ 11, но все еще есть некоторые случаи, с которыми я не могу понять, как правильно их обрабатывать, не имея раздела goto Cleanup, который, наконец, выполняет очистку.

В частности, я думаю о программировании для Windows, где иногда мне хочется утечки дескрипторов, а иногда нет.

Если у меня есть функция, которая выглядит примерно так:

PROCESS_INFORMATION p;
if (!CreateProcess(... &p))
{
  throw windows_error(GetLastError());
}

DWORD r = WaitForSingleObject(h, 5000);
if (r == WAIT_TIMEOUT || r == WAIT_FAILED)
{
  // terminate process, close handles
  throw ...;
}

if (!SetEnvironmentVariable(...))
{
  // terminate process, close handles
  throw windows_error;
}

(a few other operations that if they fail i have cleanup to do).

return process handle;

Я действительно не понимаю, как unique_ptr может мне здесь помочь, если я не использую release() на unique_ptr после всего if, чтобы указать на успех / сказать unique_ptr не убирать его. (Я могу создать специальный удалитель для unique_ptr, чтобы он правильно очищал дескрипторы окон). Однако у меня вопрос в том, является ли такое использование release() на интеллектуальных указателях «правильным» для случаев, когда я хочу передать выделенную память / дескрипторы обратно вызывающему абоненту? Есть ли способ лучше? Думаю, возврат shared_ptr тоже может сработать ...


person atanamir    schedule 19.04.2014    source источник
comment
Когда вы говорите «утечка к вызывающему», вы имеете в виду возвращаемое значение функции?   -  person svick    schedule 19.04.2014
comment
В моем конкретном случае прямо сейчас я сохраняю его в частном поле HANDLE в своем классе. Но да, в общем, я имею в виду либо для вызывающего, либо время жизни за пределами области действия функции.   -  person atanamir    schedule 19.04.2014
comment
Описание под кодом очень похоже на знаменитый Scope Guard (Alexandrescu / Marginean). Вот исходная статья, но есть являются лучшими реализациями, доступными для C ++ 11. Однако может быть более подходящим создать / использовать класс с исключительной ответственностью за управление этим ресурсом процесса.   -  person dyp    schedule 20.04.2014


Ответы (1)


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

Если ресурс можно копировать (например, shared_ptr), вы можете просто скопировать его куда угодно, и все готово. Когда ваша функция возвращается, уничтожается только ваша копия ресурса.

Если ресурс не копируемый (например, unique_ptr), вам необходимо переместить его, что и переместить семантика в C ++ 11 - это все.

Когда вы перемещаете ресурс в новое место, старое место становится пустым, поэтому при вызове его деструктора делать нечего.

Если вы передаете ресурс с помощью return, вам не нужно делать ничего особенного, return перемещается автоматически, если это возможно.

Например:

std::unique_ptr<resource> get_resource()
{
    std::unique_ptr<resource> result(new resource());

    if (result->is_something_wrong())
    {
        throw std::exception();
    }

    return result;
}

Если вы хотите передать ресурс в поле или что-то в этом роде, вам нужно явно указать, что вы хотите переместить его, используя _ 6_:

class resource_user
{
    void init_resource()
    {
        std::unique_ptr<resource> result(new resource());

        if (result->is_something_wrong())
        {
            throw std::exception();
        }

        resource_ = std::move(result);
    }

    std::unique_ptr<resource> resource_;
};
person svick    schedule 21.04.2014
comment
Перемещение должно быть по умолчанию, поскольку оно обычно быстрее, чем копирование, и имеет другое значение (исходный ресурс используется повторно). Кроме того, существует третий случай для ресурсов, которые нельзя ни копировать, ни перемещать, например std::mutex (но они встречаются гораздо реже). - person dyp; 21.04.2014
comment
@dyp Я не думаю, что это должно быть по умолчанию. Когда я делаю a = b, я не ожидал, что b станет пустым. - person svick; 21.04.2014
comment
Это не то, что я имел ввиду. У OP есть функция, которая получает ресурс и хочет получить этот ресурс (вернуть его). Это должно быть сделано с помощью перемещения по умолчанию. - person dyp; 21.04.2014