【问题标题】:C++ vector passed by value: did I get it right?C++ 向量按值传递:我做对了吗?
【发布时间】:2015-11-06 13:25:40
【问题描述】:

假设我有一个这样的结构:

struct typeA
{
    long first;
    string second
    double third;
};

如果我声明

typeA myArray[100];

然后将 myArray 存储在堆栈中,消耗 sizeof(typeA)*100 字节的垃圾数据(至少在我存储一些实际数据之前)。

每当我将此数组作为参数传递时,我总是会传递一个指向堆栈中第一个元素的第一个元素的指针。所以指针从一个栈移到另一个栈。

但如果我声明

vector<int> myVector (4, 100);

那么 myVector 对象实际上存储在堆栈中,它包含指向存储在堆中的 4*sizeof(int) 字节数组的第一个元素的指针,其中存储实际数据。所以指针从栈到堆。

每当我将此向量作为参数传递时,如果我像这样将它添加到参数列表中:

vector<int> parameterVector

函数获取myVector 对象的副本并将其存储在堆栈中。

但如果我这样做:

vector<int> &parameterVector

函数获得了一个对 myVector 的引用存储在堆栈中,所以我现在有一个存储在堆栈中的变量,它引用了一个也存储在堆栈中的 myVector 对象,它包含一个指向数组的指针存储在堆中的实际元素。

这是正确的吗?

我有几个疑问:

  1. 实际元素是否存储在堆中的静态数组(从 C 继承的数组,用方括号表示)?
  2. myVector 对象是只有一个指向第一个元素的指针,还是有多个指向每个元素的指针?
  3. 因此,按值传递向量不会造成太大问题,因为唯一被复制的是向量对象,而不是实际元素。是这样吗?
  4. 如果我弄错了整个事情并且在按值传递向量参数时也复制了实际元素,那么为什么 C++ 允许这样做,因为它不鼓励使用静态数组? (据我所知,静态数组总是作为对第一个元素的引用传递)。

谢谢!

【问题讨论】:

  • 如果你通过值传递一个向量,那么元素也会被复制
  • 声明vector&lt;typeA&gt; &amp;parameterVector 声明parameterVector 是对typeA 元素向量的引用,而不是指针。
  • 关于vector&lt;typeA&gt; myVector (4, 100),这不是一个有效的声明。它尝试创建一个包含四个元素的向量,每个元素都初始化为100。但是您不能将typeA 对象初始化为100
  • 是的,没错@Joachim Pileborg。对术语混淆和无效声明感到抱歉。我刚刚编辑了它。
  • 在 C++ 中,将变量传递给函数的默认方式是按值传递。这就是intchar* 等的传递方式。向量并不特殊,它们只是遵循相同的规则。奇怪的是 C 样式的数组,因为它与其他所有内容都不一致。

标签: c++ vector heap-memory


【解决方案1】:

实际元素是否存储在堆中的静态数组(从 C 继承的数组,用方括号表示)?

通常,向量的元素使用动态数组存储在免费存储中,例如

some_type* some_name = new some_type[some_size]

myVector 对象是只有一个指向第一个元素的指针,还是有多个指向每个元素的指针?

通常,向量将具有指向第一个元素的指针、大小变量和容量。它可能有更多,但这些是实现细节,标准没有定义。

因此,按值传递向量不会造成太大问题,因为唯一被复制的是向量对象,而不是实际元素。是这样吗?

没有。复制向量是一个 O(N) 操作,因为它必须复制向量的每个元素。如果没有,那么您将有两个向量使用相同的底层数组,如果一个被破坏,那么它将从另一个下面删除该数组。

【讨论】:

  • “把它当作一个按值传递的对象” 除了修改它会影响函数之外的东西。
  • "把它当作一个按值传递的对象" -- no : 把它当作一个按引用传递的对象,因为事情就是这样。
【解决方案2】:
  1. 实际元素是否存储在堆中的静态数组(从 C 继承的数组,用方括号表示)?

std::vector&lt;&gt; 将在堆上为您使用标准分配器的所有元素分配内存。它将管理该内存并在必要时重新分配。所以不,没有静态数组。这更像是在 C 中处理动态数组,但没有所有陷阱。

如果您正在寻找 C 阵列的现代替代品,请查看 std::array&lt;&gt;。请注意,std::array&lt;&gt; 也会复制所有元素。通过引用传递,如果这就是你的意思。

  1. myVector 对象是只有一个指向第一个元素的指针,还是有多个指向每个元素的指针?

std::vector 通常是一个指向第一个元素的指针、一个大小和一些内部使用的位。但细节实际上是特定于实现的。

  1. 因此,按值传递向量不会造成太大问题,因为唯一被复制的是向量对象,而不是实际元素。是这样吗?

没有。每当向量对象被复制到另一个向量对象时,所有元素都会被复制。

  1. 如果我弄错了整个事情并且在按值传递向量参数时也复制了实际元素,那么为什么 C++ 允许这样做,因为它不鼓励使用静态数组? (据我所知,静态数组总是作为对第一个元素的引用传递)。

“静态数组”是 C-Legacy。您不应该在新代码中再使用它们。如果您想通过引用传递向量,请这样做,并且不会复制任何内容。如果您希望移动矢量,请移动它,而不是复制它。每当你告诉编译器,你想复制一个对象,它就会复制。

好的,为什么会这样?

C 行为在某种程度上与语言的其他部分不一致。传递int时会被复制,传递struct时会被复制,传递指针时会被复制,但是传递数组时不会复制数组,而是指针到它的第一个元素。

所以C++的方式更加一致。按值传递会复制所有内容,而按引用传递则不会。使用 C++11 移动构造函数,可以通过移动对象来传递对象。这意味着,原始向量将被留空,而新向量将接管原始内存块的责任。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-25
    • 1970-01-01
    • 1970-01-01
    • 2020-04-30
    • 1970-01-01
    • 2017-12-26
    • 1970-01-01
    相关资源
    最近更新 更多