Я искал некоторый код, над которым я работаю, и у нас есть эквивалент этого:
AutoPtr<A> x;
...
/// x gets initialized
...
B* y = new B(x.Detach());
Где AutoPtr — наша версия auto_ptr, а Detach () возвращает собственный указатель и сбрасывает себя. Также B () становится владельцем x.
Теперь я понял, что это приведет к утечке x, если new сгенерирует std :: bad_alloc, поэтому я изменил код так:
AutoPtr<A> x;
...
/// x gets initialized
...
B* y = new B(x.Get());
x.Detach();
Но потом я понял, что если B () «владеет» указателем, и во время его создания возникает исключение, он должен позаботиться об удалении самого параметра (или он должен быть?), Так что x будет удален дважды, один раз с помощью B () и один раз деструктором х.
Теперь, есть ли идиома C ++, которая решает эту проблему, например, делает код, который вызывает конструкторов, ответственных за очистку параметров? Кажется, большая часть кода, который я видел, не делает этого …
Кажется, очевидным решением является временный AutoPtr<A>
конструктору B
:
AutoPtr<B> y(new B(AutoPtr<A>(x));
(это также добавляет контроль ресурсов для B*
вернулся из new B()
).
B
конструктор позвонит x.Detach()
инициализировать все, что нужно для инициализации с A*
, Если исключение происходит в любой момент, AutoPtr<A>
выпустит объект.
Если вы хотите сохранить A
объект управляется x
в случае исключения вы можете передать AutoPtr<A>&
конструктору B
вместо.
…он должен позаботиться об удалении самого параметра (или должен?)
Нет, не должно.
B
не существует, пока конструктор не завершится, и если он не существует, он не должен претендовать на владение чем-либо (в какой-то степени; если сам конструктор что-то сделал, это также должно быть безопасным).
Идиома C ++ — не использовать необработанные указатели для владения (в том числе для y
)! B
должен принять AutoPtr
в качестве аргумента, так что вызывающая сторона может отказаться от владения этим способом. Это цель std::unique_ptr
а также std::move
:
std::unique_ptr<A> x;
std::unique_ptr<B> y(new B(std::move(x)));
Также обратите внимание, что на самом деле new
не следует использовать так же; вместо этого используйте make_*
коммунальные услуги:
auto y = std::make_unique<B>(std::move(x));
Но это отсутствует в настоящее время в качестве надзора.
Примерно так, может быть:
B* y = new B();
y->Attach(x.Detach());
или же
B* y = new B();
(*y) = x;