智能指针 - 用于栈分配变量的unique_ptr

6
我有以下简单的代码,我还没有找到一个使用智能指针来处理这种简单情况的线程,而是复制对象:
int main()
{
    int i = 1;

    std::unique_ptr<int> p1(&i);
    *p1 = 2;

    return 0;
}

这会导致指针超出作用域时出现_BLOCK_TYPE_IS_INVALID错误。如果我调用p1.release(),代码就可以正常工作,我就不会遇到这个错误。我以为这样的指针足够智能,可以处理悬空指针?
另一种选择是如果我有i的副本,那么就不会出现上述错误:
std::unique_ptr<int> p1(new int(i));

这里使用智能指针的优势是什么,我不想执行本地拷贝?
如果我使用原始指针:
int i = 1;
int *p1 = &i;
*p1 = 2;

即使我不写代码,也不会出现错误:

p1 = nullptr;
2个回答

15

std::unique_ptr用于处理动态分配的内存;当您构造一个std::unique_ptr时,它会“拥有”指针。

您的int i;在栈上,因此不是动态分配的;它的所有权不能从栈中被转移。当unique_ptr析构函数试图删除您给它的指针(因为它认为没有人再拥有它),您会得到该错误,因为delete只能用于使用new创建的指针。

鉴于您提供的简短示例,我不知道这是在什么上下文中...但对于堆栈分配的变量,您应该使用原始指针(或进行副本,如您所说)。


谢谢你们两位,这很有道理。另外,为什么我不能只做p1 = nullptr而不是p1.release()呢?我知道release()返回一个指向托管对象的指针并释放所有权。p1.reset()替换了托管对象,所以对于堆栈分配的变量来说是非法的,这很有道理。但是为什么p1 = nullptr可以编译通过,但会引发异常呢? - c0der
2
使用=unique_ptr赋值,基本上相当于调用reset,并销毁先前持有的指针。使用release放弃指针的所有权,但不销毁它。 - qxz
p1=nullptr 是一种赋值方式,p1 应该清理其指针并持有 nullptr。这是所有者需要做的。p1.release() 是为了放弃其“所有权”,将其持有的指针返回给其他人,并停止进行管理,因此不会对其进行清理。 - Chen OT

4

@qzx提供的答案是您报告的代码问题的主要原因。

关于std::unique_ptr<>的使用,还有一件事情。 您应该始终尝试使用std::make_unique<>()来创建一个std::unique_ptr。 它有时会通过提供适当的编译错误,帮助您正确地使用该指针。

例如,如果您将上面的代码更改为使用std::make_unique<>,则如下所示:

int i = 1;
std::unique_ptr<int> p1 = make_unique<int>(&i);

那么大多数编译器会报错。
can't convert int* to int

使用副本,将创建一个全新的整数,其所有权被 std::unique_ptr<> 所取得,这将有助于您。

int i = 1;
std::unique_ptr<int> p1 = make_unique<int>(i);
*p1 = 2;

这段代码运行正常。


在这种情况下,使用智能指针与使用对i的引用int &p = i没有优势,因为当&p超出范围时会被取消引用。我猜在类声明中使用智能指针更好。make_unique在c++14及以上版本中可用?我的VS2012编译器无法识别它。 - c0der

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接