为什么std :: unique_ptr构造函数接受外部指针?

时间:2020-09-03 03:07:57

标签: c++

我对cpp相对较新,并且正在学习有关智能要点的信息。我想知道以下情况:

为什么要构建一个允许std::unique_ptr的{​​{1}}?

只允许使用带有右值的lvalue来构造结构来避免邪恶的事情会更安全吗?

std::unique_ptr

我意识到您必须疯狂地编写类似的东西,但是我很高兴编译器为此大吼大叫。所以我不禁要问,为什么unique_ptr的左值初始化是一件事情?

编辑: @aler egal用户更加无聊地表达了我的想法:

“”原则上,您可以有一个构造器std::unique_ptr<int> createInt() { int* a = new int(99); std::unique_ptr<int> foo(a); delete a; return foo; } ,该构造器假定ptr拥有所有权,然后将其设置为unique_ptr<int>(int*&& ptr)。在此特定示例中,这将防止自由使用(因为d被迫null,因为在空指针上调用delete无效),但这将是一个非常奇怪的反模式。”

2 个答案:

答案 0 :(得分:6)

这是dangling pointer是什么的经典示例! 与看起来像的智能指针相反,它们只是普通(原始)指针的包装,后者可以自行管理内存并为您提供一些附加功能。

请考虑以下示例:

int* someFunc() {
    int* ptr;
    int* ptr2 = ptr;
    delete ptr;
    return ptr2;
}

这就是您要做的事情。
制作它们以便可以始终代替原始使用原始指针;意思是,它们也可以悬挂!因此,如果不允许它们进行左值初始化,那就是您不能使用智能指针的一个用例,尽管我同意,但您不可以使用其中任何一个!

上面带有智能指针的代码将完全是您尝试过的。

现在,C ++完全把逻辑留给您了。如果您想用脚射击自己,那就继续吧,C ++不会吠叫。 C ++不检查内存使用情况,缓冲区溢出以及已删除内存的访问。简而言之,这是C ++程序员的工作,如果他/她想用脚射击自己,他/她可以自由地射击!


此外,通常建议在ctor上使用std::make_ptr(),因为前者已启用例外

答案 1 :(得分:2)

有一个众所周知的设计原则,建议设计不仅要保持简单,还要保持愚蠢。为最简单的可能实现增加复杂性需要证明。在这种情况下,建议的理由是避免以下邪恶的事情:

std::unique_ptr<int> createInt() {
    int* a = new int(99);
    int* b = a;
    std::unique_ptr<int> foo(b);
    delete a;
    return foo;
}

糟糕。看起来我改变了一些东西(引入了一个新变量b),但是同样的恶果也在那里。仅...以这种形式,强制移动构造无济于事。即使在构造后将b设置为null,a的值也将保持不变。 (这种形式需要类似于从unique_ptr中移动shared_ptr的构造,但是出于充分的理由,这种构造器不存在。)

因此,您最终会给构造函数的实现(它需要使提供的指针无效)和构造函数的使用(foo(a)需要变成foo(std::move(a)))带来一些麻烦,但是问题并没有真正解决。如果该语言的最高目标是安全性,那么复杂性可能仍然是合理的。但是,C ++将性能(您只为使用的东西付费)放在安全性之上。


C ++的许多安全功能都可以确保良好的完成,例如释放内存。几乎没有什么事情可以阻止不好的事情完成,例如两次释放相同的内存。有些不好的事情会触发某些编译器的警告,但最终,程序员会得到程序员想要的东西。