About C++ destructors
If you increase the warning level on your compiler, you'll get a hint that your class contains pointers but you're not defining Sequence(const Sequence&)
or operator=(const Sequence&)
(see What is The Rule of Three?).
Because you don't provide the copy constructor or assignment operator, the compiler provides these for you, which perform member-wise assignment.
When you call s1 = Sequence(3,"s1");
, you are doing the following (this may be unexpected to a Java developer):
- Creating a new, temporary,
Sequence
of three with "s1" as its name - Assigning this to
s1
, which:- sets
si._content
to be the a pointer to the new array of threeints
just created, leaking the old one of 10. - sets
si._count
to3
- sets
si._name
to"s1"
- sets
- The temporary (and not
s1
) is then destroyed (in your actual output above, you see "s1" being destroyed twice), leaving_content
pointing to free'd memory (which is why you see garbage on the second call tos1.show()
).
If you declare an assignment operator like this, you'll get something closer to your expected output:
Sequence& operator =(const Sequence& rhs)
{
if (this != &rhs)
{
delete [] _content;
_count = rhs._count;
_content = new int[_count];
_name = rhs._name + " (copy)";
for (int i = 0; i < _count ; ++i)
{
_content[i] = rhs._content[i];
}
}
return *this;
}
You won't, however, see:
destructor ---abc
...because you don't destroy s1
while its _name
contains "abc"
.
s1
is destroyed when it goes out of scope at the closing }
, which is why you see the second destructor call. With your code, this calls delete[]
on s1._content
a second time (it was deleted under the temporary, you'll recall). This is likely to result in a crash right at the end of your program.
I added " (copy)"
to _name
in my assignment operator to help to illustrate what is happening here.
Please also take a look at What is the copy-and-swap idiom?, which is a very neat way to deal with classes with raw pointers. This will also generate the output you desire as the instance of s1
with _name
of "abc"
gets swap
ped out and destroyed. I've implemented this here, along with a few other little improvements so that you can see it working.
N.B: The canonical way of creating an instance of a class is:
Sequence s1; // Default constructor. Do not use parentheses [http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.2]!
Sequence s2(3, "s2") // Constructor with parameters
C++ objects are rather different from Java objects, and you're running into a common point of confusion among those new to C++. Here is what's happening:
Sequence s1 = Sequence();
This creates a new Sequence, s1, with the default constructor (EDIT: at least that's what's happening in the printout above, although as several commenters have pointed out, it's perfectly valid for this to create a temporary Sequence which is then assigned to s1 via the copy constructor instead).
s1.show();
This prints the data on s1.
s1 = Sequence(3,"s1");
This is where things get a bit confusing. In this case, what happens is the following:
- A new anonymous Sequence object is constructed with the parameters 3,"s1"
- This anonymous object is copied (by value) to s1, using operator= (the copy operator)
- The anonymous Sequence object falls out of scope, and is deleted
Next, the last
s1.show();
calls show() on the original s1 object again, but its data is now a copy of the anonymous data.
Finally, s1 falls out of scope, and is deleted.
If you want objects that behave more like Java objects, you need to handle them as pointers, e.g.
Sequence *s1 = new Sequence(); // constructor
s1->show(); // calling a method on a pointer
delete s1; // delete the old one, as it is about to be assigned over
s1 = new Sequence(3,"s1"); // assign the pointer to a new Sequence object
s1->show();
delete s1;
If you want to make the memory management a bit easier, look into boost::shared_ptr, which provides reference-counted (rather than garbage-collected) automatic memory management.
As simple as I can:
Sequence s1 = Sequence()
: Default constructed Sequence (not copy constructor), no temporary, no destructor called.
s1.show()
: Prints the values in s1._content
.
s1 = Sequence(3,"s1");
: Creates a temporary, uses the implicit copy constructor to assign the values to s1. Deletes the temporary, calling the destructor, and hence invalidating the pointer (_content) in s1
and the temporary.
s1.show()
: Undefined behavior, as it is printing from an invalid pointer.
Then as s1 goes out of scope, it attempts to delete s1._content
; more undefined behavior.