std :: unique_ptr :: get()的奇数返回行为

时间:2016-03-14 19:06:59

标签: c++ c++11 unique-ptr

使用原始指针和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* carPtr{nullptr};
    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!

我没有看到差异。在两个变种我基本上做同样的事情。我错过了什么?

2 个答案:

答案 0 :(得分:3)

在第二个“变体”中,您将一个非空指针值分配给carPtr

//Variant 2: 
std::unique_ptr<Car>car = std::make_unique<Car>();
carPtr = car.get();
car.reset();

这使得carPtr不是空指针。

因此,输出表明它不是nullpointer。这在大多数系统中是可以预期的。但由于它正式未定义行为使用这个现在悬空的指针值,即使只是为了检查它是否为空而检查它,输出原则上可以是任何东西。你甚至可以获得可怕的红鼻窦守护效果。例如。

指针悬空,指的是没有对象,因为它所引用的对象已经通过car.reset()调用被破坏。

为了为第一个变体创建漂亮的未定义行为,

//Variant 1: 
func(carPtr);

...只需从

更改功能签名
void func(Car* carPtr)

void func(Car*& carPtr)

通过引用传递参数,以便函数可以更改实际参数。

现在函数调用将carPtr更改为悬空指针值,当您尝试检查它是否为空时,随后的UB。

答案 1 :(得分:2)

你的第一个变体与你的第二个没有任何共同之处。

在第一个变体中,您创建了一些完全独立的unique_ptr(本地到func函数),指向一些完全独立的Car对象。然后你摧毁它们。函数内部的carPtr = car.get();基本上是无操作,无法执行任何操作。因此,func函数与main中的任何内容完全分离,并且不会影响main中的任何内容。

第二种变体非常不同。在main中,您创建了一个指向unique_ptr对象的Car。然后你get()指向该对象的指针,并将其存储在carPtr的{​​{1}}中。这会更改maincarPtr的值,并更改程序的行为。