std::unique_ptr::get() 的奇怪 return 行为

Odd return behaviour of std::unique_ptr::get()

我在使用原始指针和 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;
}

变体 1 和变体 2 基本上做同样的事情:unique_ptr returns 对象的地址指向指针,然后对象被删除。但是,由于我不明白的某些原因,第二次检查的输出有所不同。

变体 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!

我看不出有什么不同。在这两个变体中,我基本上都在做同样的事情。我错过了什么?

在您的第二个 "variant" 中,您将非空指针值分配给 carPtr:

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

这使得 carPtr 不是空指针。

因此,输出表明它不是空指针。这在大多数系统上都是意料之中的。但是,由于正式 Undefined Behavior 使用这个现在悬挂的指针值,即使只是为了检查它是否为空而检查它,输出原则上可以是任何东西。您甚至可以获得可怕的红色鼻腔守护进程效果。例如。


指针悬空,未引用任何对象,因为它引用的对象已通过 car.reset() 调用销毁。


为了也为第一个变体创建良好的未定义行为,

//Variant 1: 
func(carPtr);

...只需将函数签名从

更改为
void func(Car* carPtr)

void func(Car*& carPtr)

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

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

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

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

中的任何内容

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