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;
有了这个,在你的情况下,我会保留一个 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
以调用 freeInstance
或 pop_back
以 return 您的原始 std::unique_ptr
。
在正常的 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;
有了这个,在你的情况下,我会保留一个 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
以调用 freeInstance
或 pop_back
以 return 您的原始 std::unique_ptr
。