std::unique_ptr::get() 的奇怪返回行为
Posted
技术标签:
【中文标题】std::unique_ptr::get() 的奇怪返回行为【英文标题】:Odd return behaviour of std::unique_ptr::get() 【发布时间】:2016-03-14 19:06:58 【问题描述】:我在使用原始指针和 std::unique_ptr.get() 时遇到了一些奇怪的行为。 给出这个例子:
#include <iostream>
class Car
public:
Car()std::cout << "car gets created\n";
~Car()std::cout << "car gets destroyed\n";
;
void func(Car* carPtr)
std::unique_ptr<Car> car = std::make_unique<Car>();
carPtr = car.get();
int main()
Car* carPtrnullptr;
std::cout << "first check: \n";
if(carPtr)
std::cout << "car Pointer is NOT assigned to nullptr!\n";
else
std::cout << "car Pointer is assigned to nullptr\n";
//Variant 1:
//func(carPtr);
//Variant 2:
//std::unique_ptr<Car>car = std::make_unique<Car>();
//carPtr = car.get();
//car.reset();
std::cout << "\nsecond check: \n";
if(carPtr)
std::cout << "car Pointer is NOT assigned to nullptr!\n";
else
std::cout << "car Pointer is assigned to nullptr\n";
return 0;
Variant 1 和 Variant 2 做的事情基本相同:unique_ptr 将对象的地址返回给指针,然后对象被删除。但是,由于某种我不明白的原因,第二次检查中的输出有所不同。
变体 1 中的输出为:
first check:
car Pointer is assigned to nullptr
car gets created
car gets destroyed
second check:
car Pointer is assigned to nullptr
变体 2 中的输出为:
first check:
car Pointer is assigned to nullptr
car gets created
car gets destroyed
second check:
car Pointer is NOT assigned to nullptr!
我看不出有什么不同。在这两种变体中,我基本上都在做同样的事情。我错过了什么?
【问题讨论】:
func
的意义何在?它不会通过引用来获取它的参数,因此赋值carPtr = ...
基本上是没有意义的,因为局部carPtr
变量会随着它超出范围而消失。
【参考方案1】:
在您的第二个“变体”中,您将非空指针值分配给 carPtr
:
//Variant 2:
std::unique_ptr<Car>car = std::make_unique<Car>();
carPtr = car.get();
car.reset();
这使得carPtr
不是空指针。
因此,输出表明它不是空指针。这在大多数系统上都是可以预料的。但是由于使用这个现在悬空的指针值是正式的未定义行为,即使只是为了检查它是否为空而检查它,输出原则上可以是任何东西。你甚至可以获得可怕的红鼻守护效果。例如。
指针悬空,没有引用任何对象,因为它所引用的对象已通过car.reset()
调用销毁。
为了也为第一个变体创建良好的未定义行为,
//Variant 1:
func(carPtr);
...只需更改函数签名
void func(Car* carPtr)
到
void func(Car*& carPtr)
它通过引用传递参数,以便函数可以更改实际参数。
现在函数调用将carPtr
更改为一个悬空指针值,当您尝试检查它是否为空时,随之而来的是UB。
【讨论】:
所以这基本上只是基于未定义行为的巧合,两种变体都打印不同的输出? 不,不是巧合。这里的 UB 是“弱”的,因为它不太可能在现代系统上体现出来。这是正式的事情。但是空值检查可能会在某些分段架构上产生硬件异常。您看到的输出的原因也如答案中所述。 @Grundkurs:不是真的。您可以更改代码以摆脱未定义的行为,但变体之间的根本区别仍然存在。根本区别在于第一个变体不从main
修改carPtr
,而第二个变体确实从main
修改carPtr
。 UB 不是重点。
现在我明白了,func-method 中的 carPtr 参数只是函数内的一个本地副本,main() 中的原始“carPtr”永远不会受到函数中分配的影响。但是检查指向托管对象的原始指针似乎是有问题的,因为它不能保证该对象仍然有效,因此检查可能会导致 UB。【参考方案2】:
您的第一个变体与您的第二个变体没有任何共同之处。
在第一个变体中,您创建了一些完全独立的unique_ptr
(局部于func
函数),指向一些完全独立的Car
对象。然后你摧毁他们两个。您在函数中的carPtr = car.get();
基本上是一个不执行任何操作的无操作。因此,您的 func
函数与 main
中的任何内容完全分离,并且不会影响 main
中的任何内容。
第二个变体非常不同。在main
中,您创建了一个指向Car
对象的unique_ptr
。然后你 get()
指向该对象的指针并将其存储在来自 main
的 carPtr
中。这会改变main
中carPtr
的值并改变程序的行为。
【讨论】:
以上是关于std::unique_ptr::get() 的奇怪返回行为的主要内容,如果未能解决你的问题,请参考以下文章