在退出作用域块时使用 std::unique_ptr 自动释放内存的情况下,为什么不直接使用堆栈?

Gab*_*les 0 c++ stack smart-pointers heap-memory unique-ptr

这是关于智能指针(例如唯一指针)的一个很好的答案:什么是智能指针,我应该何时使用?.

这是他们提供的一个示例,作为唯一指针的最简单用法:

void f()
{
    {
       std::unique_ptr<MyObject> ptr(new MyObject(my_constructor_param));
       ptr->DoSomethingUseful();
    } // ptr goes out of scope -- 
      // the MyObject is automatically destroyed.

    // ptr->Oops(); // Compile error: "ptr" not defined
                    // since it is no longer in scope.
}
Run Code Online (Sandbox Code Playgroud)

然而,这引出了一个问题:在这种情况下,目标是简单地删除对象(释放内存),当它超出范围时,唯一指针指向,为什么不把整个对象放在堆栈上呢? , 像这样??

void f()
{
    {
       MyObject myobj(my_constructor_param);
       myobj.DoSomethingUseful();
    } // myobj goes out of scope -- 
      // and is automatically destroyed.

    // myobj.Oops(); // Compile error: "myobj" not defined
                     // since it is no longer in scope.
}
Run Code Online (Sandbox Code Playgroud)

在我看来,唯一的逻辑可能是某些对象太大以至于可能会溢出堆栈,因为堆栈似乎限制在几十 KB 到几 MB(C/C++ 程序的最大堆栈大小),而堆可以是数百 GB!

这是什么逻辑?在此让我深入了解这个看似不必要的唯一指针用例。我错过了什么?

有关的:

  1. “要记住的堆栈的另一个特性是,可以存储在堆栈上的变量的大小有限制(随操作系统而异)。分配在堆上的变量不是这种情况。” ( https://gribblelab.org/CBootCamp/7_Memory_Stack_vs_Heap.html )

n31*_*159 5

虽然这本身并不是一个糟糕的有用示例,但它会产生一些细微的变化。

  1. 多态性
struct Base { void blah() { std::cout << "Base\n";}};
struct Derived : Base { void blah() {std::cout << "Derived\n";}};

void blub(bool which) { 
    std::unique_ptr<Base> ptr = which ? new Base : new Derived;
    ptr->blah();
}
Run Code Online (Sandbox Code Playgroud)
  1. 非标删除器
{ 
    auto close = [] (FILE* fp) { fclose(fp);};
    std::unique_ptr<FILE, decltype(close)> ptr(fopen("name"), close);
} // closes file
Run Code Online (Sandbox Code Playgroud)
  1. 动态数组(可以说是使用向量)
{ 
    std:: unique_ptr<int[]> ptr( new int [n]); 
    // From C++14 on, prefer if it is no problem to value-initialize the array
    auto ptr = std::make_unique<int[]>(n);
    // From C++20 on, there is no reason for the naked new
    auto ptr = std::make_unique_for_overwrite<int[]>(n);
    // is equivalent to the first line
}
Run Code Online (Sandbox Code Playgroud)

编辑:这对于大数组也是合理的,即使在编译时知道大小。如果数组的大小真的很大(这应该非常非常罕见)并且您真的不想冒堆栈溢出的风险,这将是一种更安全的可能性。但很可能std::vector仍然是更好的选择。只有当您的对象类型既不可移动也不可复制时,向量才会出现问题(因为它在必要时无法重新分配自己,因此您基本上可以调用不修改成员函数)。

  1. 有条件地创建对象。(仅在 C++11 和 14 中,之后使用std::optional
void blah (bool smth)
{
    std::unique_ptr<T> opt;
    if (smth) {
        opt = std::unique_ptr<T>(new T);
    }
}
Run Code Online (Sandbox Code Playgroud)