0

当我们使用new创建一个对象时, 它被分配在堆上。但是,我们正在实例化的类的成员会发生什么?例如,

class foo {
 Bar x;
 Bar *y;

 foo() {
   x = 10;
   y = new Bar();
 }
}

这里,x 是一个对象,而 y 是 Bar 的一个实例。它们都分配在堆上吗?所以如果 foo F的一个对象是在一个方法中本地创建的,那么当 F 超出范围时y会发生什么?

另外,如果F是在堆上创建的,我们什么时候可以得出F悬空的结论(没有人指向它)?因为,可能没有对 F 的引用,但可能对Y的引用。

4

4 回答 4

6

它们与父对象一起骑行,即,它们是动态分配的或具有自动存储持续时间,具体取决于父对象的初始化方式。但这并不一定意味着您需要分别释放每个。

x在您的情况下,当父对象存在时将自动释放。但是,y是动态分配的。因此,它需要单独的释放。任何对 的调用new必须在某个时间点后跟对 的调用delete。这个基本规则将帮助你推理这些情况。

顺便说一句,应该使用称为 RAII(资源分配即初始化)的模式来管理动态分配对象的解除分配(不过不要忘记三法则!)。您还应该养成使用初始化列表而不是使用构造函数的主体来初始化对象的习惯,即

class foo {
 Bar x;
 Bar *y;

 foo() : x(10), y(new Bar()) { }

 // who deallocates y here?  Again, look into RAII
}
于 2012-07-30T19:28:19.893 回答
2

它们都分配在堆上吗?

是的。

所以如果 foo F 的一个对象是在一个方法中本地创建的,那么当 F 超出范围时 y 会发生什么?

什么都没有——“y”指向的实例将保持不变,因为您没有析构函数来提供清理。它实际上会成为内存泄漏,除非有其他东西引用它(并在以后清理它)。

另外,如果 F 是在堆上创建的,我们什么时候可以得出 F 悬空的结论(没有人指向它)?

只要没有任何东西直接指向它,它就会悬空。其他指向的东西y不会改变F不再可达的事实。

这就是为什么在这种情况下你真的应该使用正确的终结。如果没有调用delete匹配每个调用new,您将泄漏内存。

于 2012-07-30T19:28:33.743 回答
0

x和都y分配在实例化器分配父对象的任何地方foo。如果foo被分配为本地,则两者都xy在堆栈上分配。如果foo是通过 分配new的,那么两者都将在堆上分配。

对象y在构造函数中设置为指向foo将始终在堆上分配,因为它是通过new在默认构造函数中分配的。

由于没有为 定义析构函数foo,因此对象y总是会泄漏。应该创建一个析构函数,以便对象foo始终指向(当不为空时)以防止内存泄漏。尽管在实践中,通常使用智能指针代替裸指针来防止默认复制分配和克隆构造函数出现问题。deleteyy

于 2012-07-30T19:40:17.357 回答
0

术语有点混乱(可能是由于 Javaland ...)在 C++ 中,变量始终是“值”,而指针的“值”是“地址”。而已。与 C# 或 Java 完全不同,其中变量是引用,所有对象都是堆上的。

将指针视为“一个对象,其值是另一个对象的地址”。那时,foo只有两个使用它创建并留在其中的成员:

  • x,这是在实例本身Bar内部创建的,并且...foo
  • y,它驻留在 foo 实例中,并且是bar 的地址

包含的地址y是什么,取决于它被初始化/分配的内容。

  • 可以nullptr
  • 可以是另一个Bar已经存在的(堆或堆栈或什么的成员,取决于你)
  • 可以是另一个新创建的 Bar (通过new,就像在您的示例中一样)
  • 可以是……甚至 x 本身(通过&x
于 2012-07-30T19:40:25.230 回答