Почему unique_ptr <T> (T *) явно?
Следующие функции не компилируются:
std::unique_ptr<int> foo()
{
int* answer = new int(42);
return answer;
}
std::unique_ptr<int> bar()
{
return new int(42);
}
Я нахожу это немного неудобным. В чем заключалось явное выражение std::unique_ptr<T>(T*)
?
Ответы
Ответ 1
Вы не хотите, чтобы управляемый указатель захватил владение необработанным указателем неявно, так как это может привести к поведению undefined. Рассмотрим функцию void f( int * );
и вызов int * p = new int(5); f(p); delete p;
. Теперь представьте, что кто-то рефакторирует f
, чтобы взять управляемый указатель (любого типа) и что неявные преобразования были разрешены: void f( std::unique_ptr<int> p );
, если неявное преобразование разрешено, ваш код будет компилироваться, но вызывать поведение undefined.
Таким же образом учтите, что указатель может быть даже не динамически распределен: int x = 5; f( &x );
...
Приобретение собственности является достаточно важной операцией, что лучше иметь ее явным: программист (а не компилятор) знает, следует ли управлять ресурсом с помощью интеллектуального указателя или нет.
Ответ 2
Короткий ответ:
Явный конструктор затрудняет запись опасного кода. Другими словами, неявный конструктор помогает вам легче писать опасный код.
Длинный ответ:
Если конструктор неявный, вы можете легко написать такой код:
void f(std::unique_ptr<int> param)
{
//code
} //param will be destructed here, i.e when it goes out of scope
//the pointer which it manages will be destructed as well.
Теперь посмотрите на опасную часть:
int *ptr = new int;
f(ptr);
//note that calling f is allowed if it is allowed:
//std::unique_ptr<int> test = new int;
//it is as if ptr is assigned to the parameter:
//std::unique_ptr<int> test = ptr;
//DANGER
*ptr = 10; //undefined behavior because ptr has been deleted by the unique_ptr!
Прочитайте комментарии. Он объясняет каждую часть фрагмента кода выше.
При вызове f()
с необработанным указателем программист может не понимать, что тип параметра f()
равен std::unique_ptr
, который будет владеть указателем и будет delete
, когда он выходит из области видимости. Программист, с другой стороны, может использовать его, и delete
он даже не понимает, что он уже удален! Все это происходит из-за неявного преобразования из исходного указателя в std::unique_ptr
.
Обратите внимание, что std::shared_ptr
имеет конструктор explicit
по той же причине.