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()
一个指向该对象的指针,并将其存储在 main
的 carPtr
中。这会更改 main
中 carPtr
的值并更改程序的行为。
我在使用原始指针和 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()
一个指向该对象的指针,并将其存储在 main
的 carPtr
中。这会更改 main
中 carPtr
的值并更改程序的行为。