我有多年的 Java 开发经历,现在我想转向 C++,我很难理解内存管理系统。
让我用一个小例子来解释一下情况:
根据我的理解,您可以在堆栈或堆上分配空间。第一个是通过声明这样的变量来完成的:
int a[5]
或
int size = 10;
int a[size]
相反,如果你想在堆上分配内存,那么你可以使用“new”命令来完成。例如像:
int *a = new int[10]; (notice that I haven't tried all the code, so the syntax might be wrong)
两者之间的一个区别是,如果它是在函数完成时在堆栈上分配的,那么空间会自动释放,而在另一种情况下,我们必须使用 delete() 显式释放它。
现在,假设我有一个这样的类:
class A {
const int *elements[10];
public void method(const int** elements) {
int subarray[10];
//do something
elements[0] = subarray;
}
}
现在,我有几个问题:
- 在这种情况下,子数组分配在堆栈上。为什么在函数方法完成后,如果我查看 elements[0] 我仍然看到子数组的数据?编译器是否翻译了堆分配中的第一个分配(在这种情况下,这是一种好的做法)吗?
- 如果我将子数组声明为“const”,那么编译器不会让我将它分配给元素。为什么不?我还以为const只是不能改变指针,其他的都没有。
- (这可能很愚蠢)假设我想分配“元素”,而不是使用固定的 10 个元素,而是使用来自构造函数的参数。是否仍然可以在堆栈中分配它,或者构造函数将始终在堆中分配它?
很抱歉提出这样的问题(对于专业的 C 程序员来说这可能看起来很愚蠢),但是 C++ 的内存管理系统与 Java 非常不同,我想避免泄漏或缓慢的代码。非常感谢!
最佳答案
a) in this case, subarray is allocated on the stack. Why after the function method has finished, if I look on elements[0] I still see the data of subarray? Has the compiler translated the first allocation in a heap allocation (and in this case, is this a good practice)?
这叫做“未定义行为”,任何事情都有可能发生。在这种情况下,subarray
保存的值仍然存在,顺便说一句,可能是因为您在函数返回后立即访问了该内存。但是您的编译器也可以在返回之前将这些值清零。你的编译器也可以将喷火龙送到你家。在“未定义行为”领域,任何事情都可能发生。
b) if I declare subarray as "const", then the compiler does not let me assign it to elements. Why not? I thought that the const only concerns the inability to change the pointer, but nothing else.
这是语言的一个相当不幸的怪癖。考虑
const int * p1; // 1
int const * p2; // 2
int * const p3; // 3
int * p4; // 4
int const * const p5; // 5
这都是有效的 C++ 语法。 1 表示我们有一个指向const int 的可变 指针。 2 表示与 1 相同(这是怪癖)。 3 表示我们有一个指向可变整数 的常量指针。 4 表示我们有一个指向可变整数 的普通旧可变指针。 5 表示我们有一个指向 const int 的const 指针。规则大致是这样的:从从右到左读取常量,除了最后一个常量,它可以在右边或左边。
c) suppose I want to allocate "elements" not with a fixed 10 elements, but with a parameter that comes from the constructor. Is it still possible to allocate it in the stack, or the constructor will always allocate it in the heap?
如果您需要动态分配,那么这将通常在堆上,但堆栈和堆的概念是依赖于实现的(即无论您的编译器供应商做什么)。
最后,如果您有 Java 背景,那么您需要考虑内存的所有权。例如,在您的方法 void A::method(const int**)
中,您将指针指向本地创建的内存,而该内存在方法返回后消失。您的指针现在指向没有人拥有的内存。最好将该内存实际复制到一个新区域(例如,A
类的数据成员),然后让您的指针指向那 block 内存。
此外,虽然 C++ 可以使用指针,但明智的做法是不惜一切代价避免使用它们。例如,在可能和适当的情况下尽量使用引用而不是指针,并对任意大小的数组使用 std::vector
类。此类还将处理所有权问题,因为将一个 vector 分配给另一个 vector 实际上会将所有元素从一个 vector 复制到另一个 vector (除了现在使用右值引用,但暂时忘记这一点)。有些人认为“裸”新建/删除是糟糕的编程习惯。
关于c++ - (C++) 当函数完成时分配在堆栈上的数组发生了什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19121820/