小智 12
您可以.
你写的是std::vector<T>,但std::vector需要两个模板参数,而不只是一个.第二个模板参数指定要使用的分配器类型,并且vector构造函数具有允许传入该分配器类型的自定义实例的重载.
因此,您需要做的就是编写一个尽可能使用您自己的内部缓冲区的分配器,并在您自己的内部缓冲区已满时回退询问默认分配器.
默认的分配器不可能希望处理它,因为它不知道可以释放哪些内存位,哪些不可以.
一个示例有状态分配器,其内部缓冲区包含不应被向量覆盖的已构造元素,包括演示大问题:
struct my_allocator_state {
void *buf;
std::size_t len;
bool bufused;
const std::type_info *type;
};
template <typename T>
struct my_allocator {
typedef T value_type;
my_allocator(T *buf, std::size_t len)
: def(), state(std::make_shared<my_allocator_state, my_allocator_state>({ buf, len, false, &typeid(T) })) { }
template <std::size_t N>
my_allocator(T(&buf)[N])
: def(), state(std::make_shared<my_allocator_state, my_allocator_state>({ buf, N, false, &typeid(T) })) { }
template <typename U>
friend struct my_allocator;
template <typename U>
my_allocator(my_allocator<U> other)
: def(), state(other.state) { }
T *allocate(std::size_t n)
{
if (!state->bufused && n == state->len && typeid(T) == *state->type)
{
state->bufused = true;
return static_cast<T *>(state->buf);
}
else
return def.allocate(n);
}
void deallocate(T *p, std::size_t n)
{
if (p == state->buf)
state->bufused = false;
else
def.deallocate(p, n);
}
template <typename...Args>
void construct(T *c, Args... args)
{
if (!in_buffer(c))
def.construct(c, std::forward<Args>(args)...);
}
void destroy(T *c)
{
if (!in_buffer(c))
def.destroy(c);
}
friend bool operator==(const my_allocator &a, const my_allocator &b) {
return a.state == b.state;
}
friend bool operator!=(const my_allocator &a, const my_allocator &b) {
return a.state != b.state;
}
private:
std::allocator<T> def;
std::shared_ptr<my_allocator_state> state;
bool in_buffer(T *p) {
return *state->type == typeid(T)
&& points_into_buffer(p, static_cast<T *>(state->buf), state->len);
}
};
int main()
{
int buf [] = { 1, 2, 3, 4 };
std::vector<int, my_allocator<int>> v(sizeof buf / sizeof *buf, {}, buf);
v.resize(3);
v.push_back(5);
v.push_back(6);
for (auto &i : v) std::cout << i << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
输出:
1 2 3 4 6
该push_back的5装配到旧的缓冲区,因此施工被绕过.当6添加,新的内存分配,一切都开始充当正常.您可以通过向分配器添加一个方法来避免该问题,以指示从该点开始,不应再绕过构造.
points_into_buffer原来是最难写的部分,我从答案中省略了这一点.从我使用它的方式来看,预期的语义应该是显而易见的.请在此处查看我的问题,以便在我的答案中提供便携式实现,或者如果您的实现允许,请使用其他问题中的一个更简单的版本.
顺便说一下,我对某些实现如何使用rebind这种方式并不是很满意,因为没有避免存储运行时类型信息以及状态,但是如果你的实现不需要,你可以做一点通过使状态成为模板类(或嵌套类)也更简单.
简短的回答是向量不能使用你的缓冲区,因为它不是那样设计的.
这也是有道理的.如果向量没有分配自己的内存,那么在添加更多项目时如何调整缓冲区的大小?它分配了一个新缓冲区,但它与旧缓冲区有什么关系呢?同样适用于移动 - 如果向量不控制自己的缓冲区,它如何将此缓冲区控制到另一个实例?
这些天 - 您不再需要将 a 包装T*在 an 中std::vector,您可以用 an 包装它std::span(在 C++20 中;在此之前 - 使用gsl::span)。跨度为您提供了标准库容器的所有便利 - 实际上,基本上是std::vector排除大小更改的所有相关功能- 具有非常薄的包装器类。这就是你想要使用的,真的。
有关跨度的更多信息,请阅读: 什么是“跨度”,何时应该使用?
| 归档时间: |
|
| 查看次数: |
1018 次 |
| 最近记录: |