0

say I have class Foo which I allocate on the heap via the 'new' keyword. Say Foo has non-pointer data member Bar. Bar, although non-pointer, is itself on the heap since it is a part of Foo. It will be properly deallocated when I delete my Foo object - even if I declare my own Foo destructor that doesn't (wont, and shouldn't) delete Bar.

  1. Is there some term for data members like Bar which, although on the heap, isn't created via the "new" keyword? For objects not stack allocated but destructor-calling handled automagically.

  2. Is Foo's default destructor still created even though the programmer has declared and defined one, that executes afterward?

  3. If not, how does Bar's destructor get called?

  4. Given the standards rule that says "the committee shall make no rule that prevents C++ programmers from shooting themselves in the foot," if I WANTED to create a memory leak on a non-pointer data member, whether stack or heap allocated, how would I do so? (Note: I'm not actually trying to do this)

4

5 回答 5

1

1)这不是魔术,也没有术语。当您为 分配空间时Foosizeof Foo包括其所有数据成员的大小,并且这些成员通常沿 4 字节边界进行字节对齐。调用时delete,分配对象前面的簿记标头将保存分配的大小,其中包括Bar数据成员。

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

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

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

你所有的问题确实表明你不知道什么newdelete在做什么。我建议阅读如何构建一个简单的 C++ 内存管理器。

于 2013-04-23T01:13:22.707 回答
1

首先,C++ 标准没有堆。它具有免费存储和自动存储。

让我们看一个与您的理论相匹配的具体示例:

struct Bar { int x[100]; }
struct Foo {
  Bar bq;
  int x;
  ~Foo() {};
};

Bar bq是任何Foo实例的成员变量。当您在免费商店中创建 aFoo时,其成员变量是Foo. 如果Foo是标准布局(如上),则基本上可以保证其成员变量分配在线性缓冲区中。在某些理论编译器中,非标准布局对象可以具有奇异的布局:但实际上它们没有。

使用上述Foo方法,不会创建默认析构函数。但是,对于 的每个析构函数Foo,内部的对象都会在 的析构函数体运行Foo后被Foo销毁。请注意,销毁指针没有任何作用——我的意思是Foo.

这不是因为调用了“自动创建的默认析构函数”——默认析构函数是~Foo() {},与您定义的相同。相反,代码在析构函数完成后自动运行。

Bar不调用make 的析构函数的唯一方法是不调用数据成员Bar。有几种方法可以做到这一点,包括创建一个大小和对齐的Bar数据缓冲区,然后放置.newBar

但是,虽然不会调用析构函数,但Bar存在于其中的内存将在回收存在时由空闲存储Foo Bar回收。

如果你想Bar泄露,你会Foo被阻止返回免费商店。但是,没有办法Foo从免费存储中分配 a 并将部分返回到Foo免费存储 - 您只能返回全部或不返回。您可以选择使用这些属性实现您自己的免费存储或您自己的手动堆,没有什么能阻止您。您甚至可以覆盖newanddelete以便当有人执行 aFoo* foo = new Foo()和执行delete foo.

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

于 2013-04-23T01:58:45.887 回答
0

好吧,您可以让操作员超载delete什么也不做,但这确实会严重影响您自己。

我想如果你真的想要你会让删除操作符调用一种内存管理器,它本身有一个指针,而不是释放内存,作为池模式的一部分或类似的东西。这位经理最后还要负责收拾烂摊子。但这真的值得吗?

编辑:对不起,我没有阅读“非指针”部分,我想这不会起作用。

于 2013-04-23T01:18:13.583 回答
0

1) 这种行为对所有数据成员都是通用的——它们包含在类中,因此无论在何处创建类的实例都存在。(指针数据成员本身也在类中,即使它们在堆的单独部分中跟踪指向的数据。)就术语而言,您可以说它们是使用面向对象的“组合”作为不同的“按值存储”从指向或引用的数据。

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

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

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

于 2013-04-23T01:18:37.710 回答
0

使用用 new 关键字初始化的引用怎么样?

又名:

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

我认为这会起作用:)

于 2013-04-23T01:20:28.883 回答