【问题标题】:How to take ownership of an object while looping over std::vector of std::unique_ptr using a range based for loop?如何在使用基于范围的 for 循环遍历 std::unique_ptr 的 std::vector 时获取对象的所有权?
【发布时间】:2020-12-20 11:02:41
【问题描述】:

我有一个std::vector<std::unique_ptr<Kind>>,我想在对其进行迭代时对其进行清理,而无需显式调用其成员的析构函数 (.reset())。

Kind 是一个沉重的结构,它的大小在迭代过程中会增加。下一个对象不需要知道以前的对象,所以我想在不需要时清理一个迭代。

我知道 vector 最终会清理干净,但是到那时,大量的 Kind 和它们动态分配的内存加起来。我正在尝试将峰值内存减少到一个元素。

我想避免reset,因为其他开发人员可能不知道动态分配,忘记在循环结束时调用 reset 并消耗内存。

我无法创建副本,

for(std::unique_ptr<Kind> t : store)

我不能像这样移动它

for(std::unique_ptr<Kind> &&t : store)

那我该怎么做呢?

#include <iostream>
#include <vector>

struct Kind{
    char a;
    char *array;
    Kind(const char c): a(c)
    {
    }
    ~Kind(){
      free(array); // internal custom deallocator.
    }
};

int main() {
    std::vector<std::unique_ptr<Kind>> store;
    store.push_back(std::make_unique<Kind>('y'));
    store.push_back(std::make_unique<Kind>('z'));

    for(std::unique_ptr<Kind> &t : store){
        // increase size of Kind.array.
        std::cout << t->a;
        // Use the Kind.array
        // clean up t automatically.
    }
    return 0;

}

【问题讨论】:

    标签: c++ for-loop unique-ptr raii ownership


    【解决方案1】:

    将元素移出向量的示例。

    int main() {
        std::vector<std::unique_ptr<Kind>> store;
        store.push_back(std::make_unique<Kind>('y'));
        for(std::unique_ptr<Kind> &t : store){
            auto tmp = std::move(t); // leaving a valid but empty entry in store
            std::cout << tmp->a;
            // clean up t automatically.
            // tmp runs out of scope and cleans up
        }
        return 0;
    }
    

    实际上与重置没有太大区别,但可能与您在实际程序中实际执行的操作相关。

    【讨论】:

    • “可能与您在实际程序中实际执行的操作相关。”谢谢你理解这个基本的东西!
    【解决方案2】:

    如何在使用基于范围的 for 循环遍历 std::unique_ptr 的 std::vector 时获取对象的所有权?

    循环引用元素,并将std::move 指向另一个元素的唯一指针。示例:

    for(std::unique_ptr<Kind> &t : store){
        std::unique_ptr<Kind> owner = std::move(t);
        // do something with newly owned pointer
    

    我要打扫

    没有必要保留旧的结构

    你可以通过重置指针来释放对象:

    for(std::unique_ptr<Kind> &t : store) {
        // do something
        t.reset();
    

    也就是说,这通常是不必要的。当向量超出范围时,它们将被自动销毁。

    我正在尝试在这里节省一些内存

    如果您在迭代时分配动态对象,这可能很有用。否则不会影响内存的峰值使用。

    【讨论】:

    • “你的尝试似乎是徒劳的。峰值内存使用不受元素早期破坏的影响。”请参阅编辑后的 ​​Q。数组大小增加,被使用,我想在继续下一个对象之前自动删除它。
    • @puio 很公平,您似乎在迭代时分配内存,所以reset 可能有用。
    • @puio 否。如果您使用单个字符分配数百万个结构并将它们存储在一个向量中,那么无论您是一个一个地删除这些结构还是一次全部删除,您都会使用高峰内存。相反,可以减少峰值内存使用的方法是在对象被使用之前不分配它。
    • aaok.. 我误解了一些事情。谢谢
    【解决方案3】:

    如果您想确保在每次迭代后立即删除实例并且您不能等到整个循环完成,您可以编写一个包装器来处理它并同时表达您的意图:

    template <typename T>
    struct Stealing {
      std::unique_ptr<T> ptr;
      Stealing(std::unique_ptr<T>& ptr) : ptr(std::move(ptr)) {
      }   
      auto operator*() {
        return ptr.operator*();
      }   
      auto operator->() {
        return ptr.operator->();
      }   
    }
    

    您可以在循环中使用它作为 unique_ptr 的替代品:

    for (Stealing<Kind> t: store) {
      // do what you like with t as if it was a std::unique_ptr
      // when t goes out of scope, so does its member -> Kind gets destroyed
    }
    

    【讨论】:

    • @puio 只是出于好奇;我到底对你做了什么?
    • 如果仔细阅读标题,这个答案可能是用问题的初始形式写的。不断的低头,坚持“等待向量清理”的固执,反对票和接近票根本没有必要。如果是这样,其他两个回答者就会加入你们的行列。在接受的答案下查看我的评论(除非它被标志删除)web.archive.org/web/20200901152037/https://stackoverflow.com/…
    • // 猜猜我的评论在这里得到了删除标记。我没有删除它。重申一下,虽然这个答案很好(我选择了旧的 for 循环并移动了 ith 成员),但我不会接受或赞成它,因为我怀恨在心。
    • @puio 很抱歉让您如此激动。如果人们不理解您要表达的内容,这可能会令人沮丧,我也经历过同样的事情。但是,如果一个问题看起来展示了XY problem,那么不要求澄清将对 OP 造成伤害。请理解,如果人们根据您的问题询问 cmets,那是因为 他们想帮助您。听着,我不在乎你是否接受我的帖子,我希望你明白,要求澄清,甚至 VTC 都是帮助人们的有用工具,没有一个是出于恶意。
    猜你喜欢
    • 1970-01-01
    • 2014-06-28
    • 2011-10-21
    • 1970-01-01
    • 1970-01-01
    • 2021-10-21
    • 2014-01-11
    • 2013-12-16
    相关资源
    最近更新 更多