如何故意泄漏非指针堆分配的对象

时间:2013-04-23 01:07:45

标签: c++ memory-leaks heap-memory

说我有类Foo,我通过'new'关键字在堆上分配。说Foo有非指针数据成员Bar。 Bar虽然是非指针,但它本身就在堆上,因为它是Foo的一部分。当我删除我的Foo对象时,它将被正确释放 - 即使我声明我自己的Foo析构函数不会(不会,也不应该)删除Bar。

  1. 对于像Bar这样的数据成员是否有一些术语虽然在堆上,但不是通过“new”关键字创建的?对于没有堆栈分配的对象,但是自动处理析构函数调用。

  2. 即使程序员已经声明并定义了一个,之后执行了Foo的默认析构函数吗?

  3. 如果没有,Bar的析构函数如何被调用?

  4. 鉴于标准规则“委员会不应制定任何阻止C ++程序员自我攻击的规则”,如果我想在非指针数据成员上创建内存泄漏,无论是堆栈还是堆分配,我该怎么办? (注意:我实际上并没有尝试这样做)

5 个答案:

答案 0 :(得分:1)

1)这不是魔术,也没有任何术语。为Foo分配空间时,sizeof Foo包括其所有数据成员的大小,这些成员通常沿4字节边界字节对齐。调用delete时,分配对象前面的簿记标题将保留分配的大小,其中包括Bar数据成员。

2)你是什么意思?如果用户创建了默认构造函数,那么编译器为什么会生成默认构造函数?

3)Bar被释放,因为它是Foo的单个分配的一部分。

4)您需要手动修改围绕您的分配的簿记结构。这会欺骗标准内存管理器认为你的分配比它小。这样做会导致未定义的行为并可能导致崩溃。

您的所有问题都表明您不知道newdelete正在做什么。我建议阅读如何构建一个简单的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部分返回到免费商店 - 您只能返回所有它或没有。您可以选择使用这些属性实现自己的免费商店或自己的手动堆,没有什么能阻止您。您甚至可以覆盖newdelete,以便当有人执行Foo* foo = new Foo()时,以及有人执行delete foo时,使用您的堆而不是免费商店。

但是,Foo占用的内存量不在您的控制范围内,也不在每个成员变量的相对位置。

答案 2 :(得分:0)

好吧,你可以让delete运算符超载,什么都不做,但这确实会让你自己陷入困境。

我想如果你真的想要你会让delete操作符调用一种带有指针的内存管理器而不是释放内存,作为池模式的一部分或类似的东西。这位经理最终还要负责清理乱七八糟的事情。但这真的值得吗?

编辑:对不起,我没有读过“非指针”部分,我想这不行。

答案 3 :(得分:0)

1)这种行为对于所有数据成员都是通用的 - 它们包含在类中,因此存在于创建类的实例的任何位置。 (指针数据成员本身也在类中,即使它们在堆的单独部分中跟踪指向数据。)在术语方面,您可以说它们是“按值”存储,使用面向对象的“组合”作为独特的来自指向或引用的数据。

2)数据成员有自己的构造函数,它们在类构造函数的主体之前运行。您可以使用初始化列表ala Class() : data_member(value) { }为数据成员构造函数提供参数。稍后,类的默认析构函数在数据成员的默认析构函数之前运行 - 这就是为什么有一个空的析构函数不会影响数据成员的销毁。

3)它自己的默认析构函数运行,并且在类的所有数据成员和基数被销毁之后,内存被释放。对于由delete触发的所有免费商店/堆变量。

4)内存泄漏是程序不再可用的对象,但是没有也不会被销毁和解除分配。唯一没有执行自动销毁/解除分配的对象是免费存储/堆分配对象,只能使用newmalloc / realloc(或调用的函数)创建例如strdup)。所以 - 你必须使用指针(你可以存储一个引用ala *new X但是它仍然会暂时处理一个指针。

答案 4 :(得分:0)

如何使用使用new关键字初始化的引用?

AKA:

class Foo
{
private:
    Bar& bar;
public:
    Foo() : bar(*new Bar())
    { }
};

我认为这会奏效:)