C++参数 堆 vs 栈

7
假设我创建了两个向量,一个在堆上,一个在栈上:
Vector<int> vector1;
Vector<int>* vector2 = new Vector<int>;

我将vector1传递给两个函数,例如foo1(Vector<int>)foo2(Vector<int>&)。我还将vector2传递给foo3(Vector<int>*)

由于我对C++还不熟悉,这里的行为差异让我感到困惑。

  1. 我可以说foo1会复制整个vector1,而foo2只会传递对vector1的引用吗?

  2. 但是,声明在堆栈上的vector1不应该无法从其他任何地方(即foo2内部)访问,除非它是在创建它的作用域内吗?

  3. 此外,在foo1foo2中修改vector1的内容会影响原始向量吗?

  4. vector1会在其作用域结束时自动销毁,还是我们需要手动删除它?


1
避免使用 new。改用 make_uniquemake_shared。否则就像 Matteo 所写的那样。 - nwp
@nwp 我从未听说过这两个,因为我相信它们是C++的一个相当新的规范,但我会去了解一下。谢谢! - Yiyuan Lee
make_shared来自C++11,make_unique原本应该是C++11的一部分,但被遗忘了,现在它属于C++14。它让C++变得更好,以至于人们感觉"它就像是一门新语言"。 - nwp
请记住,std::vector(和其他容器)具有实现细节,您通常不必担心。 在Ubuntu 17.10(64位)上,sizeof(std::vector <T>)始终为24个字节,无论是T,元素数量还是reserve的情况。向量中的元素最终在动态内存上结束。因此,通过值传递vector1将创建一个24字节的std:vector <T>实例到自动存储器,并且它的24字节将通过复制从vector1复制的元素的动态分配进行初始化和设置。 - 2785528
3个回答

8
我说的没错,对于foo1函数,整个vector1都会被复制,而对于foo2函数,只会传递vector1的引用到函数中。
但是,vector1在栈上声明,从理论上讲应该只能在它创建的作用域内访问(即foo2函数内部无法访问)。不过你可以自由地传递它的地址(通过引用或指针),只有名称不可在外部访问。当然,只要函数没有返回,它就会继续存在,所以将其指针返回会导致错误(因为它将指向一个不存在的对象)。
这与在堆上分配内存不同,后者没有限定的生命周期或自动删除 - 它由你自己控制。这是一把双刃剑:你可以拥有具有自定义生命周期的对象,但必须记住在不再需要时实际delete��们。
此外,在foo1foo2函数内修改vector1的内容是否会影响原始向量? foo1的参数是一个新的、独立的向量,对其进行的任何修改都将局限于函数内部。相反,foo2的参数指向vector1,因此它会影响原始向量(实际上,引用通常被描述为其他对象的“别名”)。
最后,vector1在其作用域结束时会自动销毁,就像任何局部变量一样。但是:vector2必须手动删除,因为本地对象只是一个没有析构函数的指针(默认情况下,它不拥有其指向的对象)。

1
如果您想通过引用传递一个vector,则可以按照foo2()的伪代码所示进行操作。 vector<int> vector1将在堆栈上分配向量的头信息,但在自由存储区(“堆”)上分配元素。
vector<int> *vect = new vector<int>;

在自由存储区上分配所有内容。

vector<int*> vect;

将在堆栈上分配vector和一堆指针,源自https://dev59.com/NWsz5IYBdhLWcg3wOlSh#8036528

至于传递给foo1函数的副本,更改将保留在函数本地。向量会在其范围结束时自动销毁。


-1

这个代码示例应该能解释所有的差异:

#include <vector>
#include <iostream>
#include <iterator>
#include <algorithm>

using namespace std;

void dumpVector(const vector<int>& v) {
    copy(begin(v), end(v), ostream_iterator<int>(cout, " "));
    cout << endl;
}

void foo1(vector<int> v) {
    cout << "I have been passed a copy of a vector. Modifications to my copy will not affect the original vector\n";
    for (auto& i : v) { i *= 2; }
    dumpVector(v);
}

void foo2(vector<int>& v) {
    cout << "I have been passed a reference to a mutable vector, which I will modify\n";
    for (auto& i : v) { i *= 2; }
    dumpVector(v);
}

void foo3(vector<int>* v) {
    cout << "I have been passed a pointer to a mutable vector, which I will modify\n";
    for (auto& i : *v) { i *= 2; }
    dumpVector(*v);
}

int main() {
    // this will be destructed automatically since it's on the stack
    vector<int> vector1 { 1, 2, 3 };

    // this will need an explicit delete
    vector<int>* vector2 = new vector<int> { 4, 5, 6 };

    foo1(vector1);
    foo2(vector1);
    foo3(&vector1);

    cout << "vector1 now contains ";
    dumpVector(vector1);

    foo1(*vector2);
    foo2(*vector2);
    foo3(vector2);
    cout << "*vector2 now contains ";
    dumpVector(*vector2);


    // always delete naked pointers. prefer the use of unique_ptr
    delete vector2;
};

输出:

Compiling the source code....
$g++ -std=c++11 main.cpp -o demo -lm -pthread -lgmpxx -lgmp -lreadline 2>&1

Executing the program....
$demo 
I have been passed a copy of a vector. Modifications to my copy will not affect the original vector
2 4 6 
I have been passed a reference to a mutable vector, which I will modify
2 4 6 
I have been passed a pointer to a mutable vector, which I will modify
4 8 12 
vector1 now contains 4 8 12 
I have been passed a copy of a vector. Modifications to my copy will not affect the original vector
8 10 12 
I have been passed a reference to a mutable vector, which I will modify
8 10 12 
I have been passed a pointer to a mutable vector, which I will modify
16 20 24 
*vector2 now contains 16 20 24 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接