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)

Brandon
  • 483
  • 3
  • 12
  • I think is is important to understand that `bar` is part of `foo`. When a class includes a pointer it is the *pointer* that is a member of `foo`, the thing the pointer points to is not (excepting pathological cases) a part of `foo` even if you `new` it in `foo::c'tor` and `delete` it in `foo::d'tor` (in which case `foo` is responsible for it and notionally owns it). – dmckee --- ex-moderator kitten Apr 23 '13 at 01:18

5 Answers5

1

1) It isn't magic, and there's no term for it. When you allocate space for Foo, the sizeof Foo includes the size of all its data members, and those members are usually byte aligned along 4 byte boundaries. When delete is called, the book-keeping header in front of your allocated object will hold the size of the allocation, which will include the Bar data member.

2) What do you mean? If a default constructor is created by the user then why would the compiler generate a default constructor?

3) Bar is freed because it is a part of a single allocation of Foo.

4) You would need to modify the book-keeping structure that surrounds your allocation by hand. This would trick the standard memory manager into thinking your allocation is smaller than it is. Doing this would result in undefined behavior and probably cause a crash.

All of your questions really show that you don't have any idea what new or delete are doing. I suggest reading up on how construct a simple C++ memory manager.

RandyGaul
  • 1,915
  • 14
  • 21
  • I meant default destructor, not constructor. – Brandon Apr 23 '13 at 01:25
  • Default ctor for Foo or Bar, and what has the user already declared? The question isn't too clear. – RandyGaul Apr 23 '13 at 01:27
  • @zeroth Okay, if the user defines a dtor for Foo, then one will not be automatically generated. One may still be generated for Bar though. – RandyGaul Apr 23 '13 at 01:33
  • But then what calls Bar's destructor? if not Foo's, then whose? – Brandon Apr 23 '13 at 01:37
  • Each data member has its destructor called in a order from highest in declared in the file, to lowest when the object it resides in is destructed. Even if you write an empty dtor for Foo, each member including Bar should have its destructor called. You don't have to explicitly call destructors for your members. – RandyGaul Apr 23 '13 at 01:40
1

First, the C++ standard has no heap. It has the free store, and automatic storage.

Lets look at a concrete example that matches your theoretical one:

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

Bar bq is a member variable of any Foo instance. When you create a Foo on the free store, its member variables are part of Foo. If Foo is standard layout (like above), you are basically guaranteed that its member variables are allocated in a linear buffer. Non standard layout objects can, in some theoretical compiler, have exotic layouts: but in practice they do not.

With the above Foo, no default destructor is created. However, for every destructor of Foo, the objects inside Foo are destroyed after the body of Foo's destructor is run. Note that destroying a pointer does nothing -- I mean the actual objects part of Foo.

This isn't because the "default destructor that is automatically created" is called -- the default destructor is ~Foo() {}, the same as what you defined. Rather, code automatically runs after the destructor finishes.

The only way to make Bar's destructor not be called would be to make the data member not be Bar. There are a few ways to do this, including creating a Bar sized and aligned buffer of data then placement newing the Bar.

However, while the destructor will not be called, the memory that Bar exists within will be recycled by the free store when the Foo Bar exists in is recycled.

If you wanted Bar to leak, you'd block Foo from being returned to the free store. There is, however, no way to allocate a Foo from the free store and return part of the Foo to the free store -- you are only allowed to return all of it or none. You could choose to implement your own free store or your own manual heap with these properties, and nothing will stop you. You can even override new and delete so that your heap is used instead of the free store when someone does a Foo* foo = new Foo(), and when someone does a delete foo.

However, the amount of memory that the Foo takes up is not within your control, nor the relative location of each member variable.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • great explanation. However, I'm not clear about how C++ has no heap. I thought "free store" and "heap" were synonymous. What's the difference? From what I've gathered, the stack is the first X bytes of memory available to a process and the heap is whatever is beyond that. – Brandon Apr 23 '13 at 05:52
  • nevermind the last question, found this: http://stackoverflow.com/questions/6161235/what-is-the-difference-between-the-heap-and-the-free-store – Brandon Apr 23 '13 at 06:05
0

Well, you can overload the delete operator to do nothing, but that would be shooting yourself in the foot quite badly indeed.

I guess if you really wanted you would have the delete operator call a kind of memory manager with a pointer on itself instead of freeing the memory, as part of a pool pattern or something like that. This manager would also be responsible for cleaning up the mess in the end. but is that really worth it?

Edit: Sorry, i didn't read the "non pointer" part, I guess this wouldn't work then.

Thibaut
  • 2,400
  • 1
  • 16
  • 28
0

1) this behaviour is common to all data members - they're included in the class so exist wherever an instance of the class is created. (Pointer data members themselves are in the class even if they track pointed-to data in a separate part of the heap.) In terms of terminology, you can say they're "stored by value" using Object Oriented "composition" as distinct from pointed-to or referenced data.

2) Data members have their own constructors that run before the class constructor's body. You can provide arguments to the data member constructors using the initialiser list ala Class() : data_member(value) { }. Later, the default destructor for the class runs before the default destructor for the data members - that's why having an empty destructor doesn't affect the data member destruction.

3) It's own default destructor runs, and after all the data members and bases of the class are destroyed the memory is deallocated. For a free-store/heap variable all that is triggered by delete.

4) A memory leak is an object which is no longer usable by the program but hasn't and won't be destroyed and deallocated. The only objects for which automatic destruction/deallocation isn't performed are free-store/heap allocated objects, which can only be created using new or malloc/realloc (or functions that call that such as strdup). So - you have to use pointers (you can store a reference ala *new X but that's still dealing with a pointer momentarily.

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
0

How about using a reference that is initialised with the new keyword?

AKA:

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

I think this will work :)

Sellorio
  • 1,806
  • 1
  • 16
  • 32
  • valgrind confirms it was indeed leaked. I'd like to point out I'm not trying to legitimately do this but understand why or how compilers know to call destructors when the programmer didn't specifically say so, and especially curious if it is possible to leak a stack allocated object's memory. This is heap allocation, and it's clear why it leaks. – Brandon Apr 23 '13 at 01:36
  • @zeroth Don't forget to tick me :) Glad I could help. Yeah there are some finer subtleties to C++, I hope this answer clarified this one. – Sellorio Apr 23 '13 at 01:51
  • By tick I assume you mean +1 to the top left corner. I don't have enough reputation to do this yet - stackoverflow newb here :) – Brandon Apr 23 '13 at 06:04
  • @zeroth actually i meant the green tick that you give the answer that best answers your questions. You will see an outline of a tick on the left of each answer in a question you ask. Generally you should end up clicking the one that answers best :) – Sellorio Apr 23 '13 at 06:11