在普通的C ++设计中,大多数对象可以通过delete
语句,free
函数或特定于库free
的库来删除。对于此类对象,unique_ptr
Deleter
实现可以是通过空基类优化消除的无状态对象。但是,某些库需要使用另一个对象(可能包含函数指针或其他某些上下文)来删除该库中的对象。
typedef struct lib_object lib_object;
struct lib_api {
lib_object (*createInstance)();
void (*freeInstance)(lib_object *o);
};
通过将unique_ptr
指针存储为自定义lib_api
中的数据成员,可以将其包含在Deleter
中,但是如果需要管理多个lib_object
个实例,例如在容器中,它会使跟踪对象的内存开销加倍。在处理这个库时,可以使用哪种模式来维护RAII原则,同时仍然保持内存效率?
答案 0 :(得分:5)
如果只有一个lib_api
对象,那么你可以让你的删除器获得一个静态指针。
如果可以有多个lib_api
对象,那么您别无选择,只能在Deleter中存储指向它的指针。
答案 1 :(得分: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;
有了这个,在你的情况下我会保留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>>
这似乎更加缓存,但可能只是你的事情。
答案 2 :(得分:0)
应该有一个更优雅的解决方案,但我会写一些像
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
以返回原始std::unique_ptr
。