一个在“堆栈”上分配的向量能否从一个函数传递到另一个函数?

6

我知道函数栈上分配的变量在函数执行完毕后就无法访问了。然而,无论如何分配,向量类型都会将其元素分配在堆上。例如:

vector<int> A;

它会在堆上分配其元素的空间,而不是栈上。

我的问题是,假设我有以下代码:

int main(int argc, char *argv[]) {
    // initialize a vector
    vector<int> A = initVector(100000);

    // do something with the vector...

    return 0;
}


// initialize the vector
vector<int> initVector(int size) {
    vector<int> A (size);  // initialize the vector "on the stack"

    // fill the vector with a sequence of numbers...
    int counter = 0;
    for (vector<int>::iterator i = A.begin(); i != A.end(); i++) {
        (*i) = counter++;
    }

    return A;
}

当我在主函数中使用向量A时,我会遇到内存访问问题吗?我尝试过几次,它们都正常工作,但我担心这可能只是运气。

我的理解是,向量A在堆上分配其元素,但它具有一些“开销”参数(可能是向量的大小)在堆栈本身上分配。因此,在主函数中使用向量可能会导致内存访问问题,如果这些参数被另一个分配覆盖。有什么想法吗?


1
根据你的逻辑,int add(int a, int b) {int r = a + b; return r} 应该和你怀疑的 initVector 有同样的问题... - user395760
vector<int> A (size); => A 存储在栈上,但它所指向的内存位置存储在堆上。size 个连续的内存位置存储在堆上,而 vector 类在堆上保存了指向开始元素的指针。让我们看看是否有人能纠正我。 - Mahesh
说实话,我对这种事情也有些困惑。每当我有任何疑问时,我也会分配向量,例如:'vector<int> *a; a=new vector<int>;' 然后返回指针。 - Martin James
3个回答

6
当你使用 "return A;" 时,你返回的是一个值,所以你得到了向量的一个副本 -- C++ 创建了一个新实例并调用了复制构造函数或运算符=。因此,在这种情况下,无论内存分配在哪里都没有关系,因为你必须复制它并销毁旧副本(一些可能的优化除外)。
向量中的数据(以及所有其他 STL 容器中的数据)也是按值移动的,因此你存储整数的副本,而不是指向它们的指针。这意味着你的对象可以在任何容器操作中被多次复制,并且它们必须正确地实现复制构造函数和/或赋值运算符。C++ 默认会为你生成这些(只需在所有成员变量上调用复制构造函数),但它们并不总是做正确的事情。
如果你想在 STL 容器中存储指针,请考虑使用共享指针包装器(std::shared_ptr 或 boost::shared_ptr)。它们将确保内存被正确处理。

1

是的,它会正常工作,因为元素的内存已经分配好了,这就是用来构建vector<int> A =变量的。然而,从性能上考虑,这不是最好的想法。

我建议将您的函数更改为以下形式

void initVector(vector<int>& a, int size) 

如需更多使用方面的参考,请查看从函数返回STL向量…[C++] 从函数返回向量

如需有关性能(使用C++11)的其他参考,请查看C++0x中调用函数返回std::vector的适当方法(移动语义)


RVO可能会使建议的更改变得不必要。但无论如何,这仍然是一个好的实践。 - Amardeep AC9MF
谢谢提供链接。我同意这是一种更好的方法。我一直对向量类的实际工作方式有疑问。 - alguru
@josephthomas 如果我使用你的方法(即通过引用传递向量),那么我是否必须使用'a = new vector<int>'来分配向量?还是有其他方法可以解决这个问题? - alguru
2
当通过引用传递时,您根本不需要分配vector<int>。 - josephthomas
@Amardeep "good practice anyway" - 我不同意。传递一个应该是返回值的引用是不清晰的,而且可能会减慢程序的速度。http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/ - Robᵩ
@Rob:我不同意在这种特殊情况下它甚至应该成为一个返回值。对于名为“initVector”的函数的引用参数向量非常清晰。在更一般的情况下,你链接的文章提出了一些很好的观点。谢谢。 - Amardeep AC9MF

0

C++ 的 vector 实际上有两个内存块,通过一个单一指针连接。第一个在栈中,第二个在堆中。因此,你可以在一个单一对象中拥有栈和堆的特性。

std::vector<int> vec;
vec.push_back(10);
vec.push_back(20);
vec.push_back(30);
std::cout << sizeof(vec) << std::endl;

一旦你运行了那段代码,你会注意到栈区域并不包含元素,但它仍然存在。因此,当你将向量从一个函数传递到另一个函数时,你需要操作栈区域,而向量将像任何其他基于栈的对象一样被复制。


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