问题描述
假设我们有一个double(值)向量和一个包含对象的向量,这些对象存储了指向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()或其他方法时阻止向量重新分配内存?
解决方法
std::vector
不允许您在保留其余部分的同时将其部分缓冲区返回堆。 std::allocator
“概念”中没有API可以做到这一点。
std::vector
保证连续存储,因此不能分段分配。
如果不需要连续存储,请使用std::deque
或手动复制的副本(双端队列使一些性能关键参数可以自由选择以供实现选择,并且不会暴露将它们配置为结束的能力-用户;因此,如果您的双端队列不适合您的用例,则可能必须自己滚动。)
std::deque
是一个固定大小的缓冲区的动态数组,外加少量的终端管理状态。它允许使用动态大小的容器和O(1)随机访问进行稳定的存储,并且容器开销仅占存储量的一小部分(缩放开销是每个块的指针,并且块大小比指针大很多倍)在我所见过的每一种实现上)。
与vector相比,缺点是存储不连续,并且iterator / []
查找具有间接的额外指针,但这将是一个奇怪的连续存储容器,可以满足您的其他需求
关键是
添加后的对象永不删除
而不是存储指向向量元素的指针/迭代器/引用,而是存储指向向量的指针 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 )
我可以禁止向量重新分配对象吗?
您可以通过不执行会使向量(可能)重新分配的操作来避免重新分配。
除了使向量为const外,没有其他方法可以阻止执行这些操作。一种解决方案是将向量设置为私有成员(并且永远不要返回指向该成员的非常量指针/引用),在这种情况下,您只需要担心成员函数不会执行这些操作即可。
问题是我不知道要存储多少个对象,也许是5个,也许是50万个
如果在插入之间不需要指针,则可以一次执行所有插入(无论插入多少),然后再获取指针,然后不再修改向量。
在其中以上是不是一种选择,如果需要的指针保持有效的一般情况下,载体是不数据结构的一个合适的选择。列表或双端队列将取决于您如何使用容器。
关于示例:拥有对象向量和指向这些对象的(包含包装器的)指针向量似乎毫无意义。