C ++:在堆上分配的对象

时间:2012-07-30 19:25:21

标签: c++

当我们使用 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 个答案:

答案 0 :(得分:6)

它们与父对象一起运行,即,它们是动态分配的,或者具有自动存储持续时间,具体取决于父进程的初始化方式。这并不一定意味着您需要单独解除分配。

在您的情况下,

x将在父对象时自动释放。但是,y是动态分配的。因此,它需要单独的释放。任何对new的致电都必须在某个时刻致电delete。这个基本规则将帮助您解释这些情况。

顺便说一句,应该使用称为RAII(Resource Allocation is Initialization)的模式来管理动态分配对象的释放(不要忘记The Rule of Three!)。您还应养成使用初始化列表而不是使用构造函数体来初始化对象的习惯,即

class foo {
 Bar x;
 Bar *y;

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

 // who deallocates y here?  Again, look into RAII
}

答案 1 :(得分:2)

  

它们都是在堆上分配的吗?

  

因此,如果foo F的对象是在方法内部创建的,那么当F超出范围时会发生什么?

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

  

另外,如果在堆上创建了F,我们何时会得出结论F是悬空(没有人指向它)?

一旦没有任何直接指向它,它就会晃来晃去。指向y的其他内容并未改变F无法再访问的事实。

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

答案 2 :(得分:0)

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

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

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

答案 3 :(得分:0)

术语中存在一些混淆(可能是由于Javaland ......) 在C ++中,变量总是“值”,而指针的“值”是“地址”。而已。 完全不同于C#或Java,其中变量是引用,并且所有对象都在堆上。

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

  • x,这是在Bar实例本身内创建的foo并且......
  • y,它位于foo实例中,并且是一个栏的地址

y中包含的地址取决于初始化/分配的内容。

  • 可以是nullptr
  • 可以是另一个Bar已经存在(堆或堆栈或成员,取决于你)
  • 可以是另一个新创建的栏(通过new,就像您的样本中一样)
  • 可以是...甚至x本身(通过&x