【发布时间】:2023-01-01 22:31:08
【问题描述】:
我有一个存储在std::vector<std::unique_ptr<Base>> 中的类层次结构。经常从这个向量中添加和删除,所以我想尝试自定义内存分配以避免对 new 和 delete 的所有调用。我只想使用 STL 工具,所以我正在尝试 std::pmr::unsynchronized_pool_resource 进行分配,然后将自定义删除器添加到 unique_ptr。
到目前为止,这是我想出的:
#include <memory_resource>
#include <vector>
#include <memory>
// dummy classes
struct Base
{
virtual ~Base() {}
};
struct D1 : public Base
{
D1(int i_) : i(i_) {}
int i;
};
struct D2 : public Base
{
D2(double d_) : d(d_) {}
double d;
};
// custom deleter: this is what I'm concerned about
struct Deleter
{
Deleter(std::pmr::memory_resource& m, std::size_t s, std::size_t a) :
mr(m), size(s), align(a) {}
void operator()(Base* a)
{
a->~Base();
mr.get().deallocate(a, size, align);
}
std::reference_wrapper<std::pmr::memory_resource> mr;
std::size_t size, align;
};
template <typename T>
using Ptr = std::unique_ptr<T, Deleter>;
// replacement function for make_unique
template <typename T, typename... Args>
Ptr<T> newT(std::pmr::memory_resource& m, Args... args)
{
auto aPtr = m.allocate(sizeof(T), alignof(T));
return Ptr<T>(new (aPtr) T(args...), Deleter(m, sizeof(T), alignof(T)));
}
// simple construction of vector
int main()
{
auto pool = std::pmr::unsynchronized_pool_resource();
auto vec = std::vector<Ptr<Base>>();
vec.push_back(newT<Base>(pool));
vec.push_back(newT<D1>(pool, 2));
vec.push_back(newT<D2>(pool, 4.0));
return 0;
}
这会编译,我很确定它不会泄漏(如果我错了请告诉我!)但是我对 Deleter 类不太满意,它必须为大小和结盟。
我首先尝试将其设为模板,以便我可以自动计算出大小和对齐方式:
template <typename T>
struct Deleter
{
Deleter(std::pmr::memory_resource& m) :
mr(m) {}
void operator()(Base* a)
{
a->~Base();
mr.get().deallocate(a, sizeof(T), alignof(T));
}
std::reference_wrapper<std::pmr::memory_resource> mr;
};
但随后每种类型的 unique_ptrs 都不兼容,向量将无法保存它们。
然后我尝试通过基类解除分配:
mr.get().deallocate(a, sizeof(Base), alignof(Base));
但这显然是个坏主意,因为释放的内存与分配的内存具有不同的大小和对齐方式。
那么,如何通过基指针解除分配而不在运行时存储大小和对齐方式? delete 似乎可以管理,所以这里似乎也应该可以。
【问题讨论】:
-
大多数分配器存储额外的信息(在返回的内存或其他地方)。
-
new还存储额外的信息,你看不到,delete使用。编译器可能会提供一些帮助来减少开销,但这既不是这里也不是那里。
标签: c++ std allocation