我可以禁止向量重新分配对象吗?
Can i forbid vector to reallocate objects?
假设我们有一个 double (values) 向量和一个包含对象的向量,这些对象存储指向 double (vars) 向量元素的指针:
class Var
{
public:
explicit Var(double* v) : _value(v) {};
~Var() {};
const double get() const { return *_value; };
void set(const double v) { *_value = v; };
private:
double* _value;
};
struct Vars
{
Vars()
{
//values.reserve(1000'000);
}
vector<double> values{};
vector<Var> vars{};
void push(const double v)
{
values.push_back(v);
vars.emplace_back(&values.back());
}
};
(添加后的对象永远不会被删除)
众所周知,当向量重新分配对象时,所有指针都会中断。
我可以预先调用 reserve(),但问题是我不知道将存储多少个对象,可能是 5 个,也可能是 500'000 个,shrink_to_fit() 无论如何都会破坏指针。
在这种情况下我可以使用双端队列,但我想知道我是否可以在调用 shrink_to_fit () 或其他方式时阻止向量重新分配内存?
Can i forbid vector to reallocate objects?
您可以通过不执行导致向量(可能)重新分配的操作来避免重新分配。
除了使向量成为 const 之外,没有其他方法可以阻止这些操作的执行。一种解决方案是使 vector 成为私有成员(永远不会 return 指向该成员的 non-const 的指针/引用),在这种情况下,您只需要担心您的成员函数不执行那些操作。
the problem is that I don't know how many objects will be stored, maybe 5, or maybe 500'000
如果不需要插入之间的指针,可以一次性完成所有插入(不管多少次),然后再获取指针,然后不再修改vector。
在一般情况下,以上不是一个选项,如果您需要指针保持有效,那么 vector 不是一个合适的数据结构选择。列表或双端队列的工作取决于您如何使用容器。
关于这个例子:拥有对象向量和指向这些对象的指针(包含包装器)的向量似乎毫无意义。
std::vector
不允许您将其缓冲区的一部分 return 保留到堆中,同时保留其余部分; std::allocator
“概念”中没有 API 可以做到这一点。
std::vector
保证连续存储,所以不能分块分配。
如果您不需要连续存储,请使用 std::deque
或 hand-rolled 副本(deque 留下一些 performance-critical 参数供实现选择,并且不公开能够将它们配置为 end-users;因此,如果您的双端队列不适合您的用例,您可能需要自己动手)。
std::deque
是固定大小缓冲区的动态数组,外加少量 end-management 状态。它允许使用动态大小的容器和 O(1) 随机访问进行稳定存储,容器开销只占存储量的一小部分(缩放开销是每个块一个指针,块大小比指针大很多倍在我见过的每一个实现上)。
与 vector 相比,它的缺点是存储不连续,迭代器/[]
查找有一个额外的间接指针,但它会是一个奇怪的连续存储容器,适合其余部分您的要求。
关键是
objects after adding are never deleted
不是将 pointers/iterators/references 存储到向量的元素,而是将指针1 存储到向量和索引。
class Var
{
public:
explicit Var(std::vector<double> * vec, size_t index)
: vec(vec), index(index) {};
double get() const { return vec->at(index); };
void set(double v) { vec->at(index) = v; };
private:
std::vector<double> * vec;
size_t index;
};
struct Vars
{
vector<double> values;
vector<Var> vars;
void push(double v)
{
vars.emplace_back(&values, values.size());
values.push_back(v);
}
};
- 指针而不是引用所以我们是 SemiRegular 类型(并且可以很容易地将
==
定义为 Regular)
假设我们有一个 double (values) 向量和一个包含对象的向量,这些对象存储指向 double (vars) 向量元素的指针:
class Var
{
public:
explicit Var(double* v) : _value(v) {};
~Var() {};
const double get() const { return *_value; };
void set(const double v) { *_value = v; };
private:
double* _value;
};
struct Vars
{
Vars()
{
//values.reserve(1000'000);
}
vector<double> values{};
vector<Var> vars{};
void push(const double v)
{
values.push_back(v);
vars.emplace_back(&values.back());
}
};
(添加后的对象永远不会被删除)
众所周知,当向量重新分配对象时,所有指针都会中断。 我可以预先调用 reserve(),但问题是我不知道将存储多少个对象,可能是 5 个,也可能是 500'000 个,shrink_to_fit() 无论如何都会破坏指针。
在这种情况下我可以使用双端队列,但我想知道我是否可以在调用 shrink_to_fit () 或其他方式时阻止向量重新分配内存?
Can i forbid vector to reallocate objects?
您可以通过不执行导致向量(可能)重新分配的操作来避免重新分配。
除了使向量成为 const 之外,没有其他方法可以阻止这些操作的执行。一种解决方案是使 vector 成为私有成员(永远不会 return 指向该成员的 non-const 的指针/引用),在这种情况下,您只需要担心您的成员函数不执行那些操作。
the problem is that I don't know how many objects will be stored, maybe 5, or maybe 500'000
如果不需要插入之间的指针,可以一次性完成所有插入(不管多少次),然后再获取指针,然后不再修改vector。
在一般情况下,以上不是一个选项,如果您需要指针保持有效,那么 vector 不是一个合适的数据结构选择。列表或双端队列的工作取决于您如何使用容器。
关于这个例子:拥有对象向量和指向这些对象的指针(包含包装器)的向量似乎毫无意义。
std::vector
不允许您将其缓冲区的一部分 return 保留到堆中,同时保留其余部分; std::allocator
“概念”中没有 API 可以做到这一点。
std::vector
保证连续存储,所以不能分块分配。
如果您不需要连续存储,请使用 std::deque
或 hand-rolled 副本(deque 留下一些 performance-critical 参数供实现选择,并且不公开能够将它们配置为 end-users;因此,如果您的双端队列不适合您的用例,您可能需要自己动手)。
std::deque
是固定大小缓冲区的动态数组,外加少量 end-management 状态。它允许使用动态大小的容器和 O(1) 随机访问进行稳定存储,容器开销只占存储量的一小部分(缩放开销是每个块一个指针,块大小比指针大很多倍在我见过的每一个实现上)。
与 vector 相比,它的缺点是存储不连续,迭代器/[]
查找有一个额外的间接指针,但它会是一个奇怪的连续存储容器,适合其余部分您的要求。
关键是
objects after adding are never deleted
不是将 pointers/iterators/references 存储到向量的元素,而是将指针1 存储到向量和索引。
class Var
{
public:
explicit Var(std::vector<double> * vec, size_t index)
: vec(vec), index(index) {};
double get() const { return vec->at(index); };
void set(double v) { vec->at(index) = v; };
private:
std::vector<double> * vec;
size_t index;
};
struct Vars
{
vector<double> values;
vector<Var> vars;
void push(double v)
{
vars.emplace_back(&values, values.size());
values.push_back(v);
}
};
- 指针而不是引用所以我们是 SemiRegular 类型(并且可以很容易地将
==
定义为 Regular)