【问题标题】:reserve() - data() trick on empty vector - is it correct?保留() - 空向量上的数据()技巧 - 正确吗?
【发布时间】:2019-12-20 07:59:52
【问题描述】:

我们知道,std::vector 在像std::vector vect(n)empty_vect.resize(n) 这样初始化时,不仅分配所需的内存量,而且用默认值初始化它(即调用默认构造函数)。这会导致不必要的初始化,特别是如果我有一个整数数组并且我想用一些无法通过任何向量构造函数提供的特定值填充它。

另一方面,Capacity 会像 empty_vect.reserve(n) 一样在调用中分配内存,但在这种情况下,向量仍然是空的。所以size()返回0empty()返回trueoperator[]产生异常。

现在,请查看代码:

{ // My scope starts here...

    std::vector<int> vect;
    vect.reserve(n);
    int *data = vect.data();

    // Here I know the size 'n' and I also have data pointer so I can use it as a regular array.
    // ...

} // Here ends my scope, so vector is destroyed, memory is released.

问题是“所以我可以将它用作数组”是否是一个安全的假设?

不管争论,我只是对上述问题感到好奇。无论如何,至于论点:

  1. 它分配内存并在函数返回时自动释放它
  2. 代码不会执行不必​​要的数据初始化(在某些情况下可能会影响性能)

【问题讨论】:

  • 所以,你想像unique_ptr&lt;int[]&gt;一样使用它。如果不需要矢量 API,则使用 unique_ptr&lt;int[]&gt;
  • data() 中没有对象,你不能这样使用那个指针。
  • 不用resize,有insertpush_back等。
  • 接受的答案是正确的,并且更好地说明了为什么要在 C++20 中执行 OP 想要执行的操作。尽管如此,我相信这个案例比许多人想象的要微妙,事实上即使在 C++03 中也有一种解决方法,请参阅stackoverflow.com/a/69141237/2791230

标签: c++ c++11 stl


【解决方案1】:

不,你不能使用它。

标准(当前草案,C++11 中的等效措辞)says in [vector.data]

constexpr T* data() noexcept;

constexpr const T* data() const noexcept;

返回[data(), data() + size()) 是有效范围的指针。 对于非空向量,data() == addressof(front())

您无法保证可以通过超出向量大小的指针进行访问。特别是,对于一个空向量,最后一句话不适用,因此您甚至无法确定您获得的是指向底层数组的有效指针。

目前无法将std::vector 与默认初始化的元素一起使用。


如 cmets 中所述,您可以改用 std::unique_ptr(需要 #inclue&lt;memory&gt;):

auto data = std::unique_ptr<int[]>{new int[n]};

这将为您提供一个 std::unique_ptr&lt;int[]&gt; 智能指针,指向动态大小的 int 数组,当 data 的生命周期结束时,该数组将自动销毁,并且可以通过移动操作转移其所有权。

它可以使用通常的指针语法直接取消引用和索引,但不允许直接指针算术。可以通过data.get() 从中获取原始指针。

不过,它不提供std::vector 接口。特别是它不提供对其分配大小的访问,并且不能被复制。


注意:我在此答案的先前版本中犯了一个错误。我使用了std::make_unique&lt;int[]&gt;,却没有意识到它实际上也执行了值初始化(ints 的初始化为零)。在 C++20 中,std::make_unique_default_init&lt;int[]&gt; 将默认初始化(因此 ints 的值不确定)。

【讨论】:

  • 如果 OP 在保留后执行 pushback(0),则 data() 将返回一个有效指针。这是否使他的假设安全(r)?
  • @engf-010 是的,指针是有效的,但对于 OP 想要做的事情并没有真正的帮助。引用的子句只说[data(), data() + size()) 是一个有效范围。因此,您最多可以做的是将size() 添加到data() (这会为您提供一个超出可能无法取消引用的范围末尾的指针)。无法保证即使超出此范围的指针算术也是有效的。
  • 从您的回复中,我认为您(和我)并不完全相信这一点,即使您在形式上是正确的。但是阅读向量中的所有重新分配子句,我只能得出结论,范围限制过于严格。
  • @engf-010 如果您超出指向对象所属的数组,则指针算术通常具有未定义的行为。 std::vector 保证初始化的元素形成一个数组,指针算术在该数组上起作用,但是在[data(), data() + size) 范围之外没有构造的int 对象,因此超出我提到的指针算术将具有未定义的行为。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-09-16
  • 1970-01-01
  • 2010-11-04
  • 2016-05-31
  • 1970-01-01
  • 2021-11-30
  • 1970-01-01
相关资源
最近更新 更多