2

For a programming assignment, we are given a template class with two members declared not as pointers, but actual objects:

Foo member;

In the constructor, I tried member = *(new Foo()); initially, but learned that, at least sometimes, it was copying the new Foo object, and therefore causing memory leaks.

I finally discovered member = Foo(), and then looked up what the difference was. I learned that member will be allocated on the stack instead of the heap, and that it will be deleted once it is out of scope. How does this work for objects, though?

Is member only deleted when the parent / class object is deleted?

I also have another question about member = *(new Foo());. I was initializing two member variables of the same type:

// Members
Foo member1;
Foo member2;

// Constructor {
    member1 = *(new Foo());
    member2 = *(new Foo());
}

For some reason it seemed member1 was not being copied and it retained the same address as the initial Foo (i.e. there was no memory leak when it was deleted). member2 however, would be copied and had a different address, and memory was leaked. Is there an explanation for this?

Jeremy
  • 1
  • 85
  • 340
  • 366
Joey
  • 23
  • 3
  • 3
    If you don't already have one, it would behoove you to get [a good introductory C++ book](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). The C++ object model is completely different from most other commonly used programming languages and it is important to understand the fundamentals of it if you are going to be writing code in C++. – James McNellis Feb 13 '11 at 05:00
  • I don't know about "completely different" but something fundamental was definitely skipped here. – Fred Nurk Feb 13 '11 at 05:02
  • @James McNellis: I would like to hear how you'd classify it as fundamentally different. The only object model you can really compare to is Java, as other commonly used languages are most likely scripting languages. Even still, the fundamental principles of pointers and allocation are most likely the same between all languages, just implemented in different ways. How they are deallocated and dereferenced, though, may be different. – vol7ron Feb 13 '11 at 05:09
  • 2
    @vol7ron: One could write a book on the subject (and I'm sure someone has...). Obviously the C++ object model is fundamentally different from the Java and .NET object models: lifetimes are deterministic and the model is value-centric not reference-centric. The C++ object model is fundamentally different from that in C as well: C has no concept of construction or destruction and all copying and assignment are effectively raw memory copies. It's still quite possible to consider scripting languages, most of which like Java and .NET have nondeterministic object lifetimes. – James McNellis Feb 13 '11 at 05:23
  • @James: I haven't programmed in C/C++ in a while, so thank you for not taking my question as rude. I'm not sure how C entered the question, as I recall it lacked `class` objects and needed to heavily relied on `structs`, but we were talking about C++. I thought the core fundamentals of object creation in C++/Java were the same and I'm not sure I understand what you mean by the lifetimes are "deterministic". Still, I think you nailed what needed to be said in the comments by saying one is value-centric and the other is reference-centric. – vol7ron Feb 13 '11 at 17:55
  • @vol7ron: No, your question didn't seem rude at all! (My answer was a bit flippant though, sorry.) Concerning determinism: in a C++ program, you can always know exactly when an object is created and when it is destroyed: there are well-defined rules that stipulate when constructors and destructors are called. Object lifetimes are thus deterministic. In Java and C#, you know when objects are created, but not when they are destroyed. An object is destroyed at some point after you are done using it by the garbage collector. Lifetimes are thus nondeterministic. – James McNellis Feb 13 '11 at 23:51

2 Answers2

4

Your analysis is incorrect. Both of these are memory leaks. What you are doing is allocating a new copy of the object, assigning the value to the member, and then discarding the pointer to the allocated memory without freeing that memory. It is a memory leak in both cases.

Now, consider the following code:

class MemberType {
  public:
    MemberType() { std::cout << "Default constructor" << std::endl; }
    MemberType(int) { std::cout << "Int constructor" << std::endl; }
};

class Example1 {
  public:
     Example1() {}
  private:
     MemberType member_;
};

class Example2 {
  public:
      Example2() : member_(5) {}
  private:
      MemberType member_;
};

int main(int argc, char** argv) {
   Example1 example1;
   Example2 example2;
   return 0;
}

This code will print both different types of constructors. Note that it did not take any initialization code at all for the member to be initialized in the default manner. Hence your new statement (or even an assignment without new) would be unnecessary in the default initialization case. When initializing members using a constructor other than the default constructor, the proper way to do this is with an initializer list. (The initializer list is what is happening with ": member_(5)" in the example.

Please see the C++ FAQ on constructors for more information about constructing objects in C++.

vol7ron
  • 40,809
  • 21
  • 119
  • 172
Michael Aaron Safyan
  • 93,612
  • 16
  • 138
  • 200
2
member = *(new Foo());

new Foo() dynamically allocates a Foo object and returns a pointer to that object. The * dereferences that pointer, giving you the Foo object. This object is then assigned to member, which involves calling the Foo copy assignment operator. By default, this operator assigns the values of each of the members of the right-hand side (the *(new Foo())) object into the left-hand side object (the member).

The problem is this: new Foo() dynamically allocates a Foo object and that object is not destroyed until you delete the pointer returned from the new. You don't save that pointer anywhere, so you've leaked the dynamically allocated object.

This is not the correct way to initialize an object.

member = Foo();

This creates a temporary, initialized Foo object and this object is assigned to member. At the end of this statement (at the ;, effectively), the temporary object is destroyed. member is not destroyed, and the contents of the temporary Foo object were copied into member, so this is exactly what you want to do.

Note that the preferred way to initialize member variables is using the initializer list:

struct C {
    Foo member1, member2;

    C() : member1(), member2() { }
};
James McNellis
  • 348,265
  • 75
  • 913
  • 977