std::unique_ptr 是否在作为参考给出范围后自动清理?

Is a std::unique_ptr cleaned up automatically after scope when given as reference?

可以使用函数指针 return,这在很多方面都很有用,但是否建议从 return 值中获取参考?

#include <iostream>
#include <memory>

using namespace std;

unique_ptr<int> give_a_number(){
    return std::make_unique<int>(6);
}

int main(){
   int& ret = *give_a_number();
   return ret;
}

根据消毒剂,没有发生泄漏:

user@comp: Scrapbook$ g++ main.cpp -fsanitize=address -g
user@comp: Scrapbook$ 

所以引用以某种方式被清理了,但我不明白为什么。 清理是如何进行的,甚至 unque_ptr 如何能够跟踪数据。 这应该被视为安全行为吗? 这是社区中公认的用法吗?

这里没有泄漏。一般来说,std::unique_ptr 拥有的对象 永远不会 泄漏,只要 .release() 不被拥有的对象调用 std::unique_ptr.

但是你的程序确实有未定义的行为,因为生命周期不正确。如果您存储对拥有对象的引用或指针,您将承担一些责任以确保正确的对象生命周期。

give_a_number() returns,按值,std::unique_ptr 拥有 int 对象。因此,此 std::unique_ptr 在语句

中具体化为临时对象
int& ret = *give_a_number();

您不要将此临时文件移动到任何永久性 std::unique_ptr 实例中。所以当临时对象在语句结束时被销毁时,它拥有的 int 也被销毁。现在你的参考是悬空的。

然后您在 return ret; 中使用悬空引用,导致未定义的行为。

您可以存储对拥有对象的引用,但如果这样做,您必须确保拥有该引用的 std::unique_ptr 实例比 int 引用更有效,以避免生命周期问题。例如。以下都可以:

int main(){
   return *give_a_number(); // `std::unique_ptr` temporary outlives return value initialization
}
int main(){
   auto ptr = give_a_number(); // `ptr` lives until `main` returns
   int& ret = *ptr;
   return ret;
}

通常像上面那样将智能指针存储在一个自动变量中,并在需要时通过 *ptr 取消引用来获取对象是更安全的。 std::unique_ptr 和它拥有的对象将一直存在到块的末尾,如果它没有被移动的话。

但它是完全正确的,例如通过引用将 *ptr 传递给函数。由于 ptr 超过了这样的函数调用,所以不会有任何问题。

如果您将 *give_a_number() 直接作为参数传递给函数,这也有效,因为 std::unique_ptr 临时文件也会比这样的调用更有效。但是在那种情况下 std::unique_ptr 和它拥有的对象将只存在到语句结束(完整表达式),而不是块结束。

unique_ptr 仅存在于带有 give_a_number 的表达式中。指向 int 的指针在 return 语句之前被销毁。

可以通过使用非 POD A 记录析构函数和构造函数调用来了解它的行为方式。

struct A
{
  A() { std::cout << "A created\n"; }
  ~A() { std::cout << "A destroyed\n"; }
};

unique_ptr<A> give_a_number(){
  return std::unique_ptr<A>(new A());
}

int main(){
  std::cout << "start\n";
  A& ret = *give_a_number();
  std::cout << "reference to removed object here\n";
  return 0;
}

输出为:

start
A created
A destroyed
reference to removed object here

我不知道为什么 sanitizer 在你的情况下不打印警报,因为我的在调用你的代码时会打印。我使用 g++ 5.3.1

==21820==ERROR: AddressSanitizer: heap-use-after-free on address 0x60200000eff0 at pc 0x000000400bd5 bp 0x7ffee5f3cac0 sp 0x7ffee5f3cab8
READ of size 4 at 0x60200000eff0 thread T0