【问题标题】:Best practice for std::auto_ptrstd::auto_ptr 的最佳实践
【发布时间】:2010-12-10 22:58:06
【问题描述】:

我刚刚习惯了使用 std::auto_ptr 的智能指针。

假设我想用 auto_ptr 和普通指针调用一个函数。

auto_ptr<uint32> data_smart(new uint32[123])]);
uint32 data_fix[123];
uint32* data_dumb = new uint32[123];

processData(data_smart);
processData(data_fix);
processData(data_dumb);

不重载的最佳做法是什么?拥有带有 uint32* 参数的 processData 函数?对于这种情况,我可以使用 .get() 将智能指针转换为 uint32* 吗?或者我应该怎么做? 提前致谢!

【问题讨论】:

  • 我担心你已经搞砸了,因为使用 auto_ptr 指向一个数组完全有理由让你着火。
  • auto_ptr 是委员会设计的经典例子。啊。改用 boost::scoped_ptr 和 boost::shared_ptr。
  • 没有。忽略阵列使用 1 秒。 std::auto_ptr 用于指示所有权语义的转移,因此您使用不正确。它通常在方法 API 中指定,以指示如果您将对象传递给方法,则该方法将获得该对象的所有权。在这种情况下,您应该使用 data_fix (并且最好通过引用来传递 processesData())

标签: c++ smart-pointers


【解决方案1】:

1.

auto_ptr<uint32> data_smart(new uint32[123])]);

不要那样做。 auto_ptr 仅适用于标量(它调用 delete 而不是 delete[])。

2。 auto_ptr 拥有它指向的对象,因此除非您想将所有权传递给该函数(在您的代码中您没有),否则该函数应该接受一个普通指针。所以你应该把调用改为:

processData(data_smart.get());

为了明确表示data_smart 继续拥有该对象。

【讨论】:

    【解决方案2】:

    编辑:Noah Roberts 对您的问题的评论是这里更大的问题,但这回答了即使示例代码错误的问题....


    ...没有重载...

    如果你想在不重载的情况下做到这一点,唯一适用于所有这些的选择是使方法采用哑指针参数。

    对于这种情况,我可以将智能指针转换为 uint32* 吗?

    没有。使用std::auto_ptr&lt;t&gt;::get()。

    【讨论】:

    • 嗯,它可以用模板来完成,这在技术上并不是“重载”。但是,当然,他们仍然遇到了严重的问题,即尝试使用错误类型的智能指针。
    【解决方案3】:

    首先,您不会使用指向数组的指针来初始化 auto_ptr。它不受支持,最终会导致内存泄漏。 std::auto_ptr 只处理单个对象。

    如果您仍想使用 std::auto_ptr,但仅用于单个对象,您需要记住 std::auto_ptr 在复制构造函数中转移所有权。这意味着如果您按值传递 data_smart,则在您调用 processData 后,您的本地 auto_ptr (data_smart) 将不会保留任何内存。

    最后,您可能想要使用 boost::scoped_array 或 boost::shared_array。

    【讨论】:

    • “你最终会出现内存泄漏”应该是“你最终会出现未定义的行为”
    • 我同意您对标准中的术语的看法,但未定义的行为是用于超出标准范围的任何内容的短语。使用“内存泄漏”作为替代不是表达未定义行为的最正式方式,但在这种情况下,我认为这是常识。
    • 对不起,Vedran,但你错了。你会混淆内存管理器。一个简单的事实是,当你应该调用 'delete[]' 时调用 'delete' 可以而且经常会像在新的东西上调用 free() 一样糟糕。混乱的内存管理器会导致废话,这正是“未定义”的定义,例如,“当我尝试在我刚刚调用的这个看似无关的函数中创建向量时,我的代码崩溃了,但只有在我运行发布时在每月的第二个星期二构建?”
    • 所以我明白了。比你纠正我。我必须承认我实际上从来没有遇到过这样的问题(因为我没有将 auto_ptr 用于数组 :)),但我想是时候清理一些 Tanenbaum 书籍了。
    【解决方案4】:

    最佳做法是不使用auto_ptr。它将在 C++0x 中弃用并由 std::unique_ptr 代替(参考:C++0x 标准草案,附录 D,第 10 段)。同时,替代方案包括std::tr1::shared_ptr 和boost::scoped_ptr。

    但您的示例是一个数组,这些指针类型不是用于数组。你可以使用boost::shared_array。

    但是标准本身没有数组智能指针。这可能是因为他们认为您应该改用std::vector(或者当您在编译时知道大小时,std::array 用于固定大小的数组)。鉴于此,您可以执行以下操作:

    std::vector<uint32> dataVector;
    data.reserve(123);
    
    // or, if the size is always 123:
    std::tr1::array<uint32, 123> dataArray;
    

    现在,你可以调用你的函数来接受普通的uint32*,因为vectors 和std::tr1::arrays 都有方法可以让你以指向 C 样式数组的指针的形式访问数据:

    processData(&dataVector[0]);
    processData(dataArray.data());
    

    如果您打算这样做,我会强烈建议添加边界检查。将第二个参数传递给processData,并带有数组的大小:

    processData(&dataVector[0], dataVector.size());
    

    如果你可以完全放弃 C 风格的指针/数组,更好的方法可能是通过引用传递:

    void processData(std::vector<uint32>& data) {
        // process the data
    }
    
    // call it like this:
    processData(dataVector);
    

    但这仅适用于vectors,不适用于std::tr1::arrays 或任何其他容器。因此,更进一步,您可以使用接受迭代器的模板:

    template <class AnIterator>
    void processData(AnIterator begin, AnIterator end) {
        for (AnIterator it = begin; it != end; ++it) {
            // process each item
        }
    }
    
    // call it like this
    processData(dataVector.begin(), dataVector,end());
    
    // or like this
    processData(dataArray.begin(), dataArray.end());
    
    // or even like this (assume c_data is a C-style array):
    processData(c_data, c_data + number_of_items_in_c_data);
    

    最后一个有效,因为指向 C 样式数组的指针可以用作迭代器。

    【讨论】:

      【解决方案5】:

      在您的情况下,使用向量是最安全的选择:

      std::vector<uint32> data(123);
      

      processData 的签名最好是:

      void processData(const std::vector<uint32> & data);
      

      不过,这个用得比较频繁:

      void processData(uint32 * bytes, int length);
      

      在这两种情况下,您都可以使用向量:

      // 1
      processData(data);
      
      // 2
      processData(data.data(), data.size());
      

      【讨论】:

      • 好的,对于快速解决方案,我将为我的数据使用自己的包装类,它是静态创建的并构造/破坏数据缓冲区。对于我的应用程序中不那么高速的部分,我将使用向量。
      【解决方案6】:

      不管你为什么要使用 auto 和dumb(如你所说的)指针指向同名函数的相同数据而不重载,auto_ptr 不能用于数组,因为它调用了错误的删除类型。

      看看这个:http://learningcppisfun.blogspot.com/2007/05/custom-deleters-with-smart-pointers.html

      还可以查看有关数组智能指针的 SO 问题:auto_ptr for arrays

      【讨论】:

        【解决方案7】:

        data_smart 变量是三个变量中最愚蠢的,这让我很开心。也就是说,当该作用域结束时,auto_ptr 析构函数将在其指针上调用delete,而不是导致UB 的delete[](这比可能来自data_dumb 的内存泄漏更糟糕)。

        所以,重点是不要对数组使用 auto_ptr,而是使用向量。

        关于真正的问题。首先,如果可能的话,使用引用参数而不是指针参数。如果这不可能,请使用裸指针,auto_ptr::get() 提供对底层指针的访问权限。

        【讨论】:

          【解决方案8】:

          忽略已经 HAMMERED 不要在数组上使用 auto_ptr。

          不重载的最佳做法是什么?

          看来您的方法不会获得所有权,所以剩下的问题是它会改变吗?

          拥有带有 uint32* 参数的 processData 函数?

          processData( uint32* ) 这是一种选择,但可能不是最好的。
          processData( uint32[123] ) 如果您不进行编辑(123 开始推送一些复制)。
          processData( uint32 &amp;[123] ) 引用并根据需要应用 const。

          对于这种情况,我可以使用 .get() 将智能指针转换为 uint32* 吗?

          您可以使用 get() 获取智能指针的指针内容,它已经“键入”,因此无需强制转换。

          除此之外: 这种原始级别的数据和操作应该在一个类中,您甚至可能不需要将应该是成员变量的内容传递给成员函数。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-10-18
            • 1970-01-01
            • 2019-04-09
            • 2020-11-02
            • 1970-01-01
            • 2021-12-08
            • 2014-10-08
            • 2014-04-21
            相关资源
            最近更新 更多