【问题标题】:unique_ptr heap and stack allocationunique_ptr 堆和堆栈分配
【发布时间】:2017-08-12 03:37:14
【问题描述】:

原始指针可以指向分配在堆栈或堆上的对象。

堆分配示例:

// heap allocation
int* rawPtr = new int(100);
std::cout << *rawPtr << std::endl;      // 100

堆栈分配示例:

int i = 100;
int* rawPtr = &i;
std::cout << *rawPtr << std::endl;      // 100

使用 auto_ptr 进行堆分配示例:

int* rawPtr = new int(100);
std::unique_ptr<int> uPtr(rawPtr);
std::cout << *uPtr << std::endl;        // 100

使用 auto_ptr 进行堆栈分配示例:

int i = 100;
int* rawPtr = &i;
std::unique_ptr<int> uPtr(rawPtr);      // runtime error

“智能指针”是否旨在用于指向堆上动态创建的对象?对于 C++11,我们是否应该继续使用原始指针来指向堆栈分配的对象?谢谢。

【问题讨论】:

    标签: c++11 pointers smart-pointers heap-memory unique-ptr


    【解决方案1】:

    智能指针通常用于指向以new 分配并以delete 删除的对象。它们不必以这种方式使用,但如果我们想猜测语言结构的预期用途,这似乎就是意图。

    您的代码在最后一个示例中崩溃的原因是“使用delete 删除”部分。当它超出范围时,unique_ptr 将尝试delete 它有一个指针指向的对象。由于它是在堆栈上分配的,因此失败。就像你写的一样,delete rawPtr;

    由于通常将智能指针与堆对象一起使用,因此有一个函数可以在堆上分配并一次性转换为智能指针。 std::unique_ptr&lt;int&gt; uPtr = make_unique&lt;int&gt;(100); 将执行第三个示例的前两行的操作。还有一个匹配的make_shared 用于共享指针。

    可以对堆栈对象使用智能指针。您所做的是指定智能指针使用的删除器,提供一个不调用delete 的删除器。因为它是一个堆栈变量,不需要做任何事情来删除它,删除器什么也做不了。有人会问,如果智能指针所做的只是调用一个什么都不做的函数,那么它的意义何在?这就是为什么您通常不会看到与堆栈对象一起使用的智能指针。但这里有一个例子显示了一些有用性。

    {
        char buf[32];
        auto erase_buf = [](char *p) { memset(p, 0, sizeof(buf)); };
        std::unique_ptr<char, decltype(erase_buf)> passwd(buf, erase_buf);
    
        get_password(passwd.get());
        check_password(passwd.get());
    }
    // The deleter will get called since passwd has gone out of scope.
    // This will erase the memory in buf so that the password doesn't live
    // on the stack any longer than it needs to.  This also works for
    // exceptions!  Placing memset() at the end wouldn't catch that.
    

    【讨论】:

      【解决方案2】:

      运行时错误是由于在从未使用new 分配的内存位置上调用了delete。

      如果已经使用dynamic storage duration 创建了对象(通常实现为在“堆”上创建),则“智能指针”将无法正常运行,如运行时错误所示。

      “智能指针”是否旨在用于动态指向 在堆上创建对象?对于 C++11,我们是否应该继续 使用原始指针指向堆栈分配的对象?

      至于应该做什么,考虑存储持续时间以及具体是如何创建对象会有所帮助。

      • 如果对象具有自动存储持续时间(堆栈),则避免获取地址并使用引用。所有权不属于指针,引用使所有权更清晰。
      • 如果对象具有动态存储持续时间(堆),则可以使用智能指针,因为它可以管理所有权。

      所以对于最后一个例子,下面的会更好(指针拥有 int):

      auto uPtr = std::make_unique<int>(100);
      

      uPtr 将具有自动存储持续时间,并在超出范围时调用析构函数。 int 将具有动态存储持续时间(堆),并将由智能指针 deleteed。

      通常可以避免使用new 和delete 并避免使用原始指针。对于make_unique 和make_shared,不需要new。

      【讨论】:

        【解决方案3】:

        “智能指针”是否旨在用于指向堆上动态创建的对象?

        它们用于堆分配的对象以防止泄漏。

        C++ 的指导方针是使用纯指针来引用 单个对象(但不拥有它)。对象的 owner 以值、在容器中或通过智能指针来保存它。

        【讨论】:

          【解决方案4】:

          “智能指针”是否旨在用于指向堆上动态创建的对象?

          是的,但这只是默认设置。请注意,std::unique_ptr 有一个 constructor(该页面上没有 (3)/(4)),它接受一个 你 以某种方式获得的指针,以及一个 你 提供。在这种情况下,唯一指针不会对堆做任何事情(除非您的删除器这样做)。

          对于 C++11,我们是否应该继续使用原始指针来指向堆栈分配的对象?谢谢。

          你应该在不“拥有”指针的代码中使用原始指针——不需要关心分配或释放;这与您是指向堆、堆栈还是其他地方无关。

          另一个使用它的地方是当您为受保护/私有成员实现一些具有复杂所有权模式的类时。

          PS:请忘记std::auto_ptr...假装它从未存在过:-)

          【讨论】:

            猜你喜欢
            • 2012-06-26
            • 2016-04-25
            • 2011-10-06
            • 2017-05-06
            • 1970-01-01
            • 2019-05-17
            • 2011-10-09
            • 2017-01-04
            • 2012-06-05
            相关资源
            最近更新 更多