为什么 shared_ptr 不允许直接赋值
Why doesn't shared_ptr permit direct assignment
所以当使用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 class 这是一个允许直接赋值的智能指针,一切正常
Why [doesn't] shared_ptr
permit direct assignment [copy initialization]?
I wonder what the rationale [is] behind it? (From a comment now removed)
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);
允许这允许您直接调用带有指针参数的函数,这很容易出错,因为您不一定在调用站点知道您正在从它创建共享指针。
void f(std::shared_ptr<int> arg);
int a;
f(&a); // bug
即使您忽略这一点,您也会在调用站点创建不可见的临时文件,并且创建 shared_ptr
是相当昂贵的。
语法:
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>();
允许将原始指针隐式转换为 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
指向的内存将被 foo()
末尾的 shared_ptr
析构函数删除。当我们在 std::cout << *bar;
中访问它时,我们现在有未定义的行为,因为我们正在取消引用已删除的指针。
在您的情况下,您直接在调用站点创建指针,所以这无关紧要,但正如您从示例中看到的那样,它可能会导致问题。
I wonder why they didn't allow a much simpler and better...
随着您变得更有经验并遇到更多编写糟糕的错误代码,您的意见将会改变。
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
}
所以当使用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 class 这是一个允许直接赋值的智能指针,一切正常
Why [doesn't]
shared_ptr
permit direct assignment [copy initialization]?
I wonder what the rationale [is] behind it? (From a comment now removed)
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);
允许这允许您直接调用带有指针参数的函数,这很容易出错,因为您不一定在调用站点知道您正在从它创建共享指针。
void f(std::shared_ptr<int> arg);
int a;
f(&a); // bug
即使您忽略这一点,您也会在调用站点创建不可见的临时文件,并且创建 shared_ptr
是相当昂贵的。
语法:
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>();
允许将原始指针隐式转换为 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
指向的内存将被 foo()
末尾的 shared_ptr
析构函数删除。当我们在 std::cout << *bar;
中访问它时,我们现在有未定义的行为,因为我们正在取消引用已删除的指针。
在您的情况下,您直接在调用站点创建指针,所以这无关紧要,但正如您从示例中看到的那样,它可能会导致问题。
I wonder why they didn't allow a much simpler and better...
随着您变得更有经验并遇到更多编写糟糕的错误代码,您的意见将会改变。
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
}