说我有类Foo,我通过'new'关键字在堆上分配。说Foo有非指针数据成员Bar。 Bar虽然是非指针,但它本身就在堆上,因为它是Foo的一部分。当我删除我的Foo对象时,它将被正确释放 - 即使我声明我自己的Foo析构函数不会(不会,也不应该)删除Bar。
对于像Bar这样的数据成员是否有一些术语虽然在堆上,但不是通过“new”关键字创建的?对于没有堆栈分配的对象,但是自动处理析构函数调用。
即使程序员已经声明并定义了一个,之后执行了Foo的默认析构函数吗?
如果没有,Bar的析构函数如何被调用?
鉴于标准规则“委员会不应制定任何阻止C ++程序员自我攻击的规则”,如果我想在非指针数据成员上创建内存泄漏,无论是堆栈还是堆分配,我该怎么办? (注意:我实际上并没有尝试这样做)
答案 0 :(得分:1)
1)这不是魔术,也没有任何术语。为Foo
分配空间时,sizeof
Foo
包括其所有数据成员的大小,这些成员通常沿4字节边界字节对齐。调用delete
时,分配对象前面的簿记标题将保留分配的大小,其中包括Bar
数据成员。
2)你是什么意思?如果用户创建了默认构造函数,那么编译器为什么会生成默认构造函数?
3)Bar
被释放,因为它是Foo
的单个分配的一部分。
4)您需要手动修改围绕您的分配的簿记结构。这会欺骗标准内存管理器认为你的分配比它小。这样做会导致未定义的行为并可能导致崩溃。
您的所有问题都表明您不知道new
或delete
正在做什么。我建议阅读如何构建一个简单的C ++内存管理器。
答案 1 :(得分:1)
首先,C ++标准没有堆。它有免费商店和自动存储。
让我们看一个与你的理论相符的具体例子:
struct Bar { int x[100]; }
struct Foo {
Bar bq;
int x;
~Foo() {};
};
Bar bq
是任何Foo
实例的成员变量。在免费商店中创建Foo
时,其成员变量是Foo
的一部分。如果Foo
是标准布局(如上所述),则基本保证其成员变量在线性缓冲区中分配。在某些理论编译器中,非标准布局对象可以具有奇异的布局:但实际上它们不具有这种布局。
使用上面的Foo
,不会创建默认的析构函数。但是,对于Foo
的每个析构函数,在Foo
的析构函数体运行后,Foo
内的对象将被销毁。请注意,销毁指针不会做任何事情 - 我的意思是实际对象是Foo
的一部分。
这不是因为调用了“自动创建的默认析构函数” - 默认析构函数是~Foo() {}
,与您定义的相同。相反,代码在析构函数完成后自动运行。
使Bar
的析构函数不被调用的唯一方法是使数据成员不是Bar
。有几种方法可以执行此操作,包括创建Bar
大小且对齐的数据缓冲区,然后new
Bar
{。}}。
但是,虽然不会调用析构函数,但当存在Bar
Foo
时,免费存储将回收Bar
中存在的内存。
如果您希望Bar
泄露,则会阻止Foo
返回免费商店。但是,无法从免费商店分配Foo
并将Foo
的部分返回到免费商店 - 您只能返回所有它或没有。您可以选择使用这些属性实现自己的免费商店或自己的手动堆,没有什么能阻止您。您甚至可以覆盖new
和delete
,以便当有人执行Foo* foo = new Foo()
时,以及有人执行delete foo
时,使用您的堆而不是免费商店。
但是,Foo
占用的内存量不在您的控制范围内,也不在每个成员变量的相对位置。
答案 2 :(得分:0)
好吧,你可以让delete
运算符超载,什么都不做,但这确实会让你自己陷入困境。
我想如果你真的想要你会让delete操作符调用一种带有指针的内存管理器而不是释放内存,作为池模式的一部分或类似的东西。这位经理最终还要负责清理乱七八糟的事情。但这真的值得吗?
编辑:对不起,我没有读过“非指针”部分,我想这不行。
答案 3 :(得分:0)
1)这种行为对于所有数据成员都是通用的 - 它们包含在类中,因此存在于创建类的实例的任何位置。 (指针数据成员本身也在类中,即使它们在堆的单独部分中跟踪指向数据。)在术语方面,您可以说它们是“按值”存储,使用面向对象的“组合”作为独特的来自指向或引用的数据。
2)数据成员有自己的构造函数,它们在类构造函数的主体之前运行。您可以使用初始化列表ala Class() : data_member(value) { }
为数据成员构造函数提供参数。稍后,类的默认析构函数在数据成员的默认析构函数之前运行 - 这就是为什么有一个空的析构函数不会影响数据成员的销毁。
3)它自己的默认析构函数运行,并且在类的所有数据成员和基数被销毁之后,内存被释放。对于由delete
触发的所有免费商店/堆变量。
4)内存泄漏是程序不再可用的对象,但是没有也不会被销毁和解除分配。唯一没有执行自动销毁/解除分配的对象是免费存储/堆分配对象,只能使用new
或malloc
/ realloc
(或调用的函数)创建例如strdup
)。所以 - 你必须使用指针(你可以存储一个引用ala *new X
但是它仍然会暂时处理一个指针。
答案 4 :(得分:0)
如何使用使用new关键字初始化的引用?
AKA:
class Foo
{
private:
Bar& bar;
public:
Foo() : bar(*new Bar())
{ }
};
我认为这会奏效:)