unique_ptr 删除器开销

unique_ptr deleter overhead

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

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 对象,那么您可以让删除器获取指向它的静态指针。

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

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

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<pair<lib_object, lib_api>> 和支持这个结构的静态删除器。

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>>

这似乎需要大量缓存,但可能正是您的菜。

应该有一个更优雅的解决方案,但我会写这样的东西

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 以 return 您的原始 std::unique_ptr