【问题标题】:Implicit conversion of pointer to std::unique_ptr in foreach loop在 foreach 循环中隐式转换指向 std::unique_ptr 的指针
【发布时间】:2019-11-20 23:12:58
【问题描述】:

我有一个 api 调用,它填充了一个原始指针数组,供调用者使用。这个函数堆分配每个原始指针但不分配数组。

无论它有多糟糕,我都无法更改此 API 函数。

调用api函数代码如下:

size_t response_count = api.getResponseCount();
std::vector<Response*> responses(response_count);
api.getResponses(responses.data());

for(auto response : responses) {
    // Do some processing with response
    delete response;
}

我想将每个响应包装在一个 unique_ptr 中,以便在循环迭代结束时仍将其清理干净,而无需显式调用 delete。理想情况下,这看起来像:

for(std::unique_ptr<Response> response : responses) {
    // Do some processing with response
    // No need to delete response, it will be cleaned up as it goes out of scope
}

这不会编译,因为编译器无法将指针转换为 unique_ptr: error: conversion from ‘Response*’ to non-scalar type ‘std::unique_ptr&lt;Response&gt;’ requested

有没有办法以这种方式将容器的每个元素转换为智能指针,还是我需要显式删除原始指针?

【问题讨论】:

  • 没问题。感谢您查看问题:)
  • 为什么api.getResponses 不接std::vector&lt;std::unique_ptr&lt;Reponse&gt;&gt;
  • 如果它是一个 C 函数,这无论如何都是一个糟糕的主意。 C 不new 和因此delete 任何东西。那么,再一次,是什么分配了在那个指针序列中返回的东西?例如。 responses 序列中的每个指针指向什么,究竟是谁分配了它,以及它是通过什么方式分配的?仅仅因为您使用 C++ 并不意味着某些任意动态指针一定来自 new,因此对于 delete 来说已经成熟。
  • 那么delete response是无效的,对吧?应该是free(response)
  • 不是向量的自定义删除器,而是循环中的 unique_ptr。自定义删除器看起来不那么干净,因此循环中的显式删除(可能带有注释?)可能是最干净的

标签: c++ memory-management unique-ptr


【解决方案1】:

我不会将每个指针包装在 unique_ptr 中,而是考虑使用 Boost ptr_vector

假设数据已分配,因此您实际上可以使用delete 删除它,代码将如下所示:

size_t response_count = api.getResponseCount();

// Unfortunately, we have to define, then resize. It has a ctor that takes a size,
// but it treats that as an amount to reserve rather than an actual size.
boost::ptr_vector<Response> responses;
responses.resize(response_count);

api.getResponses(responses.c_array());

for(auto response : responses) {
    // Do some processing with response
}

...当responses 超出范围时,它将删除它包含的指针指向的所有对象。如有必要,您可以指定一个 Allocator 类来定义如何分配和删除对象。

参考

https://www.boost.org/doc/libs/1_71_0/libs/ptr_container/doc/ptr_container.html

【讨论】:

    【解决方案2】:

    虽然不是很好的做法,但您可以使用非显式构造函数从 unique_ptr 派生。

    template<typename P>
    struct MakeUnique : std::unique_ptr<P> {
        MakeUnique(P* p) : std::unique_ptr<P>(p) {}
    };
    
    

    然后可以这样使用:

    for ( MakeUnique<Response> resp : responses ) {
        ...
    }
    

    可能是最接近单线的东西。查看工作版本here

    【讨论】:

    • 我建议对代码进行编辑,使其使用模板并更加通用。我喜欢这个解决方案,因为它是最小样板文件并且不依赖于 boost。
    【解决方案3】:

    您可以为 vector&lt;response*&gt; 创建一个包装器,用于分发唯一指针

    struct wrapper{
    
        struct iterator {
            iterator( std::vector<response*>::iterator it ) : it_(it){}
            friend bool operator!=( iterator const& lhs, iterator const &rhs ){ return lhs.it_ != rhs.it_; }
            void operator++(){ ++it_;}
            std::unique_ptr<response> operator*(){ return std::unique_ptr<response>(*it_); }
        private:   
            std::vector<response*>::iterator it_;
        };
    
        wrapper( std::vector<response*>& rs ) : rs_{rs} {}
    
        iterator begin() const { return iterator{rs_.begin()}; }
        iterator end() const { return iterator{rs_.end()}; }
    
    private:
        std::vector<response*>& rs_;
    };
    

    然后您可以像这样迭代响应:

    for( auto resp : wrapper( responses ) ){...
    }
    

    查看工作版本here

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-05-25
      • 2021-03-11
      • 1970-01-01
      • 1970-01-01
      • 2019-02-02
      • 1970-01-01
      • 1970-01-01
      • 2018-09-25
      相关资源
      最近更新 更多