为什么shared_ptr不允许直接赋值

时间:2016-05-17 12:00:15

标签: c++ c++11 shared-ptr smart-pointers

因此,当使用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类,它是一个智能指针,允许直接赋值,一切正常

5 个答案:

答案 0 :(得分:28)

语法:

shared_ptr<Type> var = new Type();

copy initialization。这是用于函数参数的初始化类型。

如果允许,你可能会意外地将一个普通指针传递给一个带有智能指针的函数。此外,如果在维护期间,有人将void foo(P*)更改为void foo(std::shared_ptr<P>),那么编译就会很好,从而导致未定义的行为。

由于此操作基本上取得了普通指针的所有权,因此必须明确地执行此操作。这就是为什么采用普通指针的shared_ptr构造函数为explicit - 以避免意外的隐式转换。

更安全,更有效的替代方案是:

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

答案 1 :(得分:20)

允许将原始指针隐式转换为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;中访问它时,我们现在有未定义的行为,因为我们正在取消引用已删除的指针。

在您的情况下,您直接在呼叫站点创建指针,因此无关紧要,但从示例中可以看出它可能会导致问题。

答案 2 :(得分:16)

允许这个允许你直接用指针参数调用函数,这很容易出错,因为你不一定知道你在调用网站上创建了一个共享指针。

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

即使你忽略了这一点,你也可以在呼叫站点创建隐形临时站点,创建shared_ptr非常昂贵。

答案 3 :(得分:8)

  

为什么[不] shared_ptr允许直接分配[复制初始化]?

因为它是explicit,请参阅herehere

  

我想知道背后的理由是什么? (现已删除评论)

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);

答案 4 :(得分:8)

  

我想知道为什么他们不允许更简单和更好......

当您变得更有经验并遇到更糟糕的错误代码时,您的意见会发生变化。

shared_ptr<>,就像编写所有标准库对象一样,尽可能使其难以导致未定义的行为(即很难找到浪费每个人时间并破坏我们生存意愿的错误)。

考虑:

#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!
}

这段代码无法编译,这是一件好事。因为如果确实如此,该程序将表现出未定义的行为。

这是因为我们要两次删除相同的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
}