当我们使用 new 创建对象时,它会在堆上分配。但是,我们正在实例化的类成员会发生什么?例如,
class foo {
Bar x;
Bar *y;
foo() {
x = 10;
y = new Bar();
}
}
这里,x是一个对象,而y是Bar的一个实例。它们都分配在堆上吗?因此,如果foo F 的对象是在方法内部创建的,那么当F超出范围时, y 会发生什么?
另外,如果在堆上创建 F ,我们何时会得出结论 F 是悬空的(没有人指向它)?因为,可能没有对F的引用,但可能会引用 Y 。
答案 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
的任何地方都分配y
和foo
。如果foo
被分配为本地,则x
和y
都将在堆栈上分配。如果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
)