【问题标题】:unique_ptr deleter overheadunique_ptr 删除器开销
【发布时间】:2016-10-10 20:30:20
【问题描述】:

在正常的 C++ 设计中,大多数对象可以通过 delete 语句、free 函数或等效于 free 的库特定的等价物来删除。对于此类对象,unique_ptrDeleter 实现可以是通过空基类优化消除的无状态对象。但是,有些库需要使用另一个对象(可能包含函数指针或其他上下文)从该库中删除对象。

typedef struct lib_object lib_object;

struct lib_api {
  lib_object (*createInstance)();
  void (*freeInstance)(lib_object *o);
};

可以通过将lib_api 指针作为数据成员存储在自定义Deleter 中来将其包装在unique_ptr 中,但是如果需要管理多个lib_object 实例,例如在容器中,它会使跟踪对象的内存开销加倍。在处理这个库时,可以使用哪种模式来维护 RAII 原则,同时仍然保持内存效率?

【问题讨论】:

  • 你能让lib_api*成为删除器类的静态成员吗​​?
  • 我认为您需要将freeInstance 存储在容器类的自定义派生类中,并且您的容器将包含lib_object*。在派生类中,您必须实现将在每个元素上调用 freeInstance 的析构函数。

标签: c++ c++11 smart-pointers raii


【解决方案1】:

如果只有一个 lib_api 对象,那么你可以让你的删除器获得一个指向它的静态指针。

如果可以有多个lib_api 对象,那么您别无选择,只能将指向它的指针存储在 Deleter 中。

【讨论】:

  • 我想过这个,看来全局变量是唯一有用的解决方案。
【解决方案2】:

我对此类对象使用自定义删除器模板。

template<typename T, T Function>
struct function_deleter
{
    template<typename U>
    auto operator()(U&& u) const noexcept(noexcept(Function(std::forward<U>(u))))
    {
        return Function(std::forward<U>(u));
    }
};

那么你就可以使用你的删除器调用free:

unique_ptr<int, function_deleter<void(*)(void*), &free>> uniq;

而且它的大小仍然等于一个指针。 live demo

在 C++17 之后,您将能够将 auto 用于非类型模板参数,将代码简化为:

template<auto Function>
struct function_deleter
{
    template<typename U>
    auto operator()(U&& u) const noexcept(noexcept(Function(std::forward<U>(u))))
    {
        return Function(std::forward<U>(u));
    }
};

unique_ptr<int, function_deleter<&call_free>> uniq;

live demo

有了这个,在你的情况下,我会保留一个 unique_ptr&lt;pair&lt;lib_object, lib_api&gt;&gt; 和支持这个结构的静态删除器。

using lib_pair = pair<lib_object, lib_api>;

void lib_free(lib_pair* p){
    p->second.freeInstance(p->first);
    delete p;
}


using unique_lib_ptr = unique_ptr<lib_pair, function_deleter<void(*)(lib_pair*), &lib_free>>

这似乎更重缓存,但可能只是你的事。

【讨论】:

    【解决方案3】:

    应该有一个更优雅的解决方案,但我会写类似

    template <class ContainerType>
    class TObjectContainer : public ContainerType {
      public:
       TObjectContainer() = default;
       TObjectContainer(const TObjectContainer&); // should call createCopy 
       TObjectContainer(TObjectContainer&&) = default;
       ~TObjectContainer()
         { for (lib_object* element : *this)
             (*freeInstance)(element);
         }
    
      private:
       void (*freeInstance)(lib_object *o);
    };
    
    typedef TObjectContainer<std::vector<lib_object*>> ObjectVector;
    

    它不使用unique_ptr,但它基本上应该可以完成这项工作。

    请注意,您可能会重载每个删除方法,例如 clear 以调用 freeInstancepop_back 以返回您原来的 std::unique_ptr

    【讨论】:

      猜你喜欢
      • 2018-10-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多