使用指针写入 std 容器

Using pointers to write to a std container

本文关键字:std 容器 指针      更新时间:2023-10-16

我希望能够创建一个固定长度的容器(vectordeque?以充当缓冲区,然后为另一个对象向量提供一个指针,指向允许它们写入的缓冲区位置。

示例(不可编译的代码(

class Item {
  *p //pointer to a place in the vector
  vector<int> values
}
vector<Item> items;
for(auto item : items) {
  for(auto value : values) {
    buffer[p] = item->value
    ++(item->p);
  }
}

但是,我不确定如何使每个Item都清楚他们应该在缓冲区中开始写入的关系。

我应该注意的是,对于items的每次迭代,最终缓冲区都有一个已知的固定大小 - 但是在函数调用之间,Items的数量可能会改变。

谢谢

如果我正确理解了这个问题(我不确定(,你应该使用索引,而不是指针或迭代器,因为它是缓冲区开头的相对偏移量,而不是缓冲区更改会使失效的绝对地址。

class Item
{
  size_t pos;  // index into the buffer
  vector<int> values;
};
vector<Item> items;
// ...
std::vector<int> buffer;
buffer.resize(N);
for (auto& item : items)
{
  assert(buffer.size() >= (item.pos + item.values.size()));
  std::copy(std::begin(item.values), std::end(item.values),
            std::begin(buffer)+item.pos);
}

这将与vectordeque一起使用作为缓冲区(或RandomAccessIterators的任何其他内容(,但是由于您似乎不需要在缓冲区的开头添加/删除元素(仅调整一次大小并分配给现有元素(,因此没有理由使用deque. Therefore you should prefer vector',这通常应该是您默认选择的容器,除非您需要其中一个的特定特征器皿。

我不知道您打算如何设置Item::pos值,也许这是有道理的:

size_t pos = 0;
for (auto& item : items)
{
  item.pos = pos;
  pos += item.values.size();
  assert(buffer.size() >= pos);
  std::copy(std::begin(item.values), std::end(item.values),
            std::begin(buffer)+item.pos);
}

这会将每个项目依次放入缓冲区中,并动态记录位置。

这甚至可以在事先不知道总缓冲区大小的情况下工作,根据需要调整缓冲区大小:

size_t pos = 0;
for (auto& item : items)
{
  item.pos = pos;
  pos += item.values.size();
  if (buffer.size() < pos)
    buf.resize(pos);
  std::copy(std::begin(item.values), std::end(item.values),
            std::begin(buffer)+item.pos);
}

由于您存储的是索引,而不是绝对地址,因此即使在调整缓冲区大小并将其内容重新定位到其他内存块后,它也会继续工作。

我应该注意,对于items的每次迭代,最终缓冲区都有一个已知的固定大小 - 但是在函数调用之间,Items的数量可能会改变。

正如我的评论中所述,您将获取并跟踪std::vector<whatever>中包含的值的指针和引用是不稳定的,只要允许通过push_back()erase()或任何其他此类操作更改向量。


不过,您可以选择

  • 参考向量的索引。即使std::vector<>需要重新分配和复制,这些也将是稳定的

  • 使用智能指针,例如 std::unique_ptr<>std::shared_ptr<>存储在std::vector<>中,而不是存储在实例副本中。

这完全取决于您的实际用例,这将是正确的方法。

正如其他人已经评论的那样,我不确定您要实现什么,但是您的原始代码与可以编译并且具有明确定义(尽管相当无用(的行为相距不远。

我想你可能打算写以下内容:

#include <vector>
#include <iostream>
struct Item
{
  std::vector<int>::iterator p;
  std::vector<int> values;
  Item() : values {'H', 'A', 'P', 'P', 'Y'}
  {
    this->p = values.begin();
  }
};
std::ostream&
operator<<(std::ostream& os, const Item& item)
{
  os << "[";
  for (std::size_t i = 0; i < item.values.size(); ++i)
    os << (i ? ", " : "") << item.values.at(i);
  os << "]";
  return os;
}
int main()
{
  std::vector<Item> items {4};
  for (auto& item : items)
    {
      for (auto value : item.values)
        {
          *(item.p) = value;  // Note: self-assignment with no effect
          ++(item.p);
        }
    }
  for (auto& item : items)
    std::cout << item << std::endl;
}

这个特定的程序表现良好,但使用这种数据结构,您迟早会搬起石头砸自己的脚。 可能有更好的解决方案。