Почему shared_ptr не разрешает прямое присвоение

Поэтому при использовании shared_ptr<Type> вы можете написать:

shared_ptr<Type> var(new Type());

Интересно, почему они не позволяли намного проще и лучше (imo):

shared_ptr<Type> var = new Type();

Вместо этого вы должны использовать .reset():

shared_ptr<Type> var;
var.reset(new Type());

Я использую класс OpenCV Ptr, который является умным указателем, который позволяет прямое назначение, и все работает отлично

Ответ 1

Проблема с возможностью неявного преобразования необработанного указателя в std::shared_ptr может быть продемонстрирована с помощью

void foo(std::shared_ptr<int> bar) { /*do something, doesn't matter what*/ }

int main()
{
    int * bar = new int(10);
    foo(bar);
    std::cout << *bar;
}

Теперь, если неявное преобразование работало, память bar указывает на то, что будет удален деструктором shared_ptr в конце foo(). Когда мы переходим к его доступу в std::cout << *bar;, у нас теперь есть поведение undefined, поскольку мы разыскиваем удаленный указатель.

В вашем случае вы создаете указатель непосредственно на сайте вызова, поэтому это не имеет значения, но, как вы можете видеть из примера, это может вызвать проблемы.

Ответ 2

Синтаксис:

shared_ptr<Type> var = new Type();

Является инициализация копирования. Это тип инициализации, используемой для аргументов функции.

Если это разрешено, вы можете случайно передать простой указатель на функцию, используя умный указатель. Более того, если во время обслуживания кто-то изменил void foo(P*) на void foo(std::shared_ptr<P>), который бы скомпилировался так же хорошо, что и поведение undefined.

Поскольку эта операция, по существу, принимает право собственности на простой указатель, эта операция должна выполняться явно. Вот почему конструктор shared_ptr, который принимает простой указатель, сделан explicit -, чтобы избежать случайных неявных преобразований.


Более безопасная и эффективная альтернатива:

auto var = std::make_shared<Type>();

Ответ 3

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

void f(std::shared_ptr<int> arg);
int a;
f(&a); // bug

Даже если вы проигнорируете это, вы создаете невидимое временное место на сайте вызова, а создание shared_ptr довольно дорого.

Ответ 4

Почему [не] shared_ptr разрешает прямое назначение [инициализация копирования]?

Потому что это explicit, см. здесь и здесь.

Интересно, что за этим стоит логика? (Из комментария теперь удалено)

TL; DR, , чтобы любой конструктор (или приведение) explicit состоял в том, чтобы помешать ему участвовать в неявных последовательностях преобразования.

Требование для explicit лучше проиллюстрировано с помощью shared_ptr<> является аргументом для функции.

void func(std::shared_ptr<Type> arg)
{
  //...
}

И вызывается как:

Type a;
func(&a);

Это будет скомпилировано и написано и нежелательно и неправильно; он не будет вести себя так, как ожидалось.

Это осложняется добавлением в микс пользовательских (неявных) преобразований (операторов литья).

struct Type {
};

struct Type2 {
  operator Type*() const { return nullptr; }
};

Тогда следующая функция (если не явная) будет компилироваться, но предлагает ужасную ошибку...

Type2 a;
func(a);

Ответ 5

Интересно, почему они не позволяли намного проще и лучше...

Ваше мнение изменится по мере того, как вы станете более опытным и столкнетесь с более плохо написанным, ошибочным кодом.

shared_ptr<>, как и все стандартные объекты библиотеки, записывается таким образом, чтобы сделать как можно более трудным поведение undefined (т.е. трудно найти ошибки, которые теряют время и разрушают нашу волю к жизни).

рассмотреть следующие вопросы:

#include<memory>

struct Foo {};

void do_something(std::shared_ptr<Foo> pfoo)
{
  // ... some things
}

int main()
{
  auto p = std::make_shared<Foo>(/* args */);
  do_something(p.get());
  p.reset();  // BOOM!
}

Этот код не может компилироваться, и это хорошо. Потому что, если это произойдет, программа будет демонстрировать поведение undefined.

Это связано с тем, что мы дважды удаляем один и тот же Foo.

Эта программа будет компилироваться и хорошо сформирована.

#include<memory>

struct Foo {};

void do_something(std::shared_ptr<Foo> pfoo)
{
  // ... some things
}

int main()
{
  auto p = std::make_shared<Foo>(/* args */);
  do_something(p);
  p.reset();  // OK
}