Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Re: mysterious destructors

85 views
Skip to first unread message
Message has been deleted

Mr Flibble

unread,
Feb 17, 2015, 7:30:37 PM2/17/15
to
On 18/02/2015 00:22, Stefan Ram wrote:
> I have written the following program:
>
> #include <iostream>
> #include <ostream>
>
> struct c
> { int v;
>
> c( int const x ): v( x )
> { ::std::cout << "constructor of instance #" << v << ".\n"; }
>
> ~c(){ ::std::cout << "destructor of instance #" << v << ".\n"; }
>
> void print(){ ::std::cout << "I am instance #" << v << ".\n"; }};
>
> int main()
> { c o = * new c( 1 );
> o.print();
> o = * new c( 2 ); /* overwrite */
> o.print(); }
>
> The program prints:
>
> constructor of instance #1.
> I am instance #1.
> constructor of instance #2.
> I am instance #2.
> destructor of instance #2.
>
> In the line marked with »/* overwrite */«, instance #1 in the
> variable »o« is overwritten with another instance of the same
> class c if I understand it correctly.
>
> I thought that this overwriting kind-of »destroys« the instance #1
> and that this might invoke the destructor of instance #1. But no,
> it does never print »destructor of instance #1.«.
>
> I am still a beginner with respect to some parts of C++, so
> I have to ask here why my expectations are wrong.

That code is mental mate; you have two memory leaks as you are create
two objects on the heap and in the first case you are *copy
constructing* 'o' on the stack with one of them and in the second case
you are *assigning* to o with one of them.

This is what you code should look like:

c o(1); // construction
o = c(2); // assignment

No heap allocation with new required.

/Flibble

Christopher Pisz

unread,
Feb 17, 2015, 7:51:04 PM2/17/15
to
Nay my friend. You have demonstrated a memory leak.
By assigning another instance to the pointer you lost the first. Raw
pointers do not automatically invoke the destructor when assigned. After
all, you may not want it destroyed, but simply to point to something
else, if you had another way of getting at it, like a second pointer
elsewhere. As is, it is dangling, lost forever.

If you would like to have that behavior, you may want to use a
std::shared_ptr or one of its kin.




-----
I have chosen to troll filter/ignore all subthreads containing the
words: "Rick C. Hodgins", "Flibble"
So, I won't be able to see or respond to any such messages
-----

Mr Flibble

unread,
Feb 17, 2015, 7:55:44 PM2/17/15
to
On 18/02/2015 00:50, Christopher Pisz wrote:

> -----
> I have chosen to troll filter/ignore all subthreads containing the
> words: "Rick C. Hodgins", "Flibble"
> So, I won't be able to see or respond to any such messages
> -----

Mr Flibble is very cross.
Message has been deleted

Christopher J. Pisz

unread,
Feb 17, 2015, 8:21:04 PM2/17/15
to
On 2/17/2015 6:59 PM, Stefan Ram wrote:
> Christopher Pisz <nos...@notanaddress.com> writes:
>> On 2/17/2015 6:22 PM, Stefan Ram wrote:
>>> int main()
>>> { c o = * new c( 1 );
>>> o.print();
>>> o = * new c( 2 ); /* overwrite */
>>> o.print(); }
>> By assigning another instance to the pointer you lost the first. Raw
>
> But both »o« and »* new c( 2 )« are not pointers, but rather
> an object and a temporary, respectively, as far as I understand it.
>
> »new c( 2 )« is a pointer, but »*« makes a temporary from
> the pointer, as far as I understand it.
>
> So, the assignment »o = * new c( 2 )« has an object on the
> left and a temporary on the right.
>

True, kind of. the <new> returns a pointer. You de-referenced it, and
assigned it to an object on the stack, whom was already default
constructed, but you saw no message for that or the assignment because
you didn't implement the default constructor or the assignment operator
yourself.

So really, you have 3 instances and the message is erroneous, because it
is actually the instance on the stack getting destroyed, while both on
the heap are leaked.


>> Nay my friend. You have demonstrated a memory leak.
>
> (I am aware that the »new« creates a memory leak missing the
> corresponding »delete«, but this was not my primary concern,
> so - for simplification - I omitted the »delete«.)
>

Oh, ok, I didn't realize you did it on purpose.

Implement the default constructor and the assignment operator with
messages and see if it makes sense.


Lőrinczy Zsigmond

unread,
Feb 18, 2015, 12:50:48 AM2/18/15
to
On 2015.02.18. 1:22, Stefan Ram wrote:
> I have written the following program:
>
> #include <iostream>
> #include <ostream>
>
> struct c
> { int v;
>
> c( int const x ): v( x )
> { ::std::cout << "constructor of instance #" << v << ".\n"; }
>
> ~c(){ ::std::cout << "destructor of instance #" << v << ".\n"; }
>
> void print(){ ::std::cout << "I am instance #" << v << ".\n"; }};

If your idea is making constructors and debuggers visible,
then you are on the right way, but it still could be improved

struct c
{ int v;

c( int const x ): v( x )
{ ::std::cout << "constructor of instance #" << v << ".\n"; }

c(const c &from): v(from.v)
{ ::std::cout << "copy constructor of instance #" << v << ".\n"; }

c& MyClass::operator= (const c &from)
{ printf ("assignment (operator=) new=%d old=%d\n",
this->v, other->v);
this->v = other= v; /* do we need this? */
return *this; }

~c(){ ::std::cout << "destructor of instance #" << v << ".\n"; }

void print(){ ::std::cout << "I am instance #" << v << ".\n"; }};

Note: either include <cstdio> or change 'printf' to a bunch of <<'s

Lőrinczy Zsigmond

unread,
Feb 18, 2015, 12:58:11 AM2/18/15
to
> struct c
> { int v;
>
> c( int const x ): v( x )
> { ::std::cout << "constructor of instance #" << v << ".\n"; }
>
> c(const c &from): v(from.v)
> { ::std::cout << "copy constructor of instance #" << v << ".\n"; }
>
> c& MyClass::operator= (const c &from)
> { printf ("assignment (operator=) new=%d old=%d\n",
> this->v, other->v);
> this->v = other= v; /* do we need this? */
> return *this; }
>
> ~c(){ ::std::cout << "destructor of instance #" << v << ".\n"; }
>
> void print(){ ::std::cout << "I am instance #" << v << ".\n"; }};
>
> Note: either include <cstdio> or change 'printf' to a bunch of <<'s

Lot of bugs here:)

c& MyClass::operator= (const c &from)
{ printf ("assignment (operator=) new=%d old=%d\n",
from.v, this->v);
this->v = from.v; /* do we need this? */
return *this; }

Paavo Helde

unread,
Feb 18, 2015, 1:44:51 AM2/18/15
to
r...@zedat.fu-berlin.de (Stefan Ram) wrote in news:destructors-
2015021...@ram.dialup.fu-berlin.de:

> I have written the following program:
> int main()
> { c o = * new c( 1 );
> o.print();
> o = * new c( 2 ); /* overwrite */
> o.print(); }
>
> The program prints:
>
> In the line marked with »/* overwrite */«, instance #1 in the
> variable »o« is overwritten with another instance of the same
> class c if I understand it correctly.

This is because you have mixed up instances with values. In your program
you have 3 instances of class c, 2 of them are leaked and so there is
only a single destructor call when the object o goes out of scope. On the
other hand, you have two values (1 and 2), copy them over across
instances and print out with misleading messages, so there is no wonder
one gets confused.

You can have an example with 2 instances (of class c) and no memory leaks
as well:

#include <iostream>
#include <ostream>
#include <memory>

struct c
{ int v;

c( int const x ): v( x )
{ ::std::cout << "constructor of instance #" << v << ".\n"; }

~c(){ ::std::cout << "destructor of instance #" << v << ".\n"; }

void print(){ ::std::cout << "I am instance #" << v << ".\n"; }};

int main()
{ std::unique_ptr<c> o(new c( 1 ));
o->print();
o = std::unique_ptr<c>(new c( 2 )); /* overwrite */
o->print(); }

This prints:
constructor of instance #1.
I am instance #1.
constructor of instance #2.
destructor of instance #1.
I am instance #2.
destructor of instance #2.



For some more fun, here is another example having 2 instances and no
memory leaks. This one is probably even more confusing ;-)

#include <iostream>
#include <ostream>

struct c
{ mutable int v;

c( int const x ): v( x )
{ ::std::cout << "constructor of instance #" << v << ".\n"; }

void operator=(const c& y) const {v = y.v;}

~c(){ ::std::cout << "destructor of instance #" << v << ".\n"; }

void print() const { ::std::cout << "I am instance #" << v << ".\n";
}};

int main()
{ const c& o = c( 1 );
o.print();
o = c( 2 ); /* overwrite */
o.print(); }

constructor of instance #1.
I am instance #1.
constructor of instance #2.
destructor of instance #2.

Tobias Müller

unread,
Feb 18, 2015, 1:51:04 AM2/18/15
to
Stefan Ram <r...@zedat.fu-berlin.de> wrote:
> I have written the following program:
>
> #include <iostream>
> #include <ostream>
>
> struct c
> { int v;
>
> c( int const x ): v( x )
> { ::std::cout << "constructor of instance #" << v << ".\n"; }
>
> ~c(){ ::std::cout << "destructor of instance #" << v << ".\n"; }
>
> void print(){ ::std::cout << "I am instance #" << v << ".\n"; }};
>
> int main()
> { c o = * new c( 1 );
> o.print();
> o = * new c( 2 ); /* overwrite */
> o.print(); }
>
> The program prints:
>
> constructor of instance #1.
> I am instance #1.
> constructor of instance #2.
> I am instance #2.
> destructor of instance #2.
>
> In the line marked with »/* overwrite */«, instance #1 in the
> variable »o« is overwritten with another instance of the same
> class c if I understand it correctly.
>
> I thought that this overwriting kind-of »destroys« the instance #1
> and that this might invoke the destructor of instance #1. But no,
> it does never print »destructor of instance #1.«.
>
> I am still a beginner with respect to some parts of C++, so
> I have to ask here why my expectations are wrong.

There seems to be a misunderstanding of the difference between objects and
pointer to objects.
I suspect that you are coming from java where this distinction is not
visible.
A reference in Java (often just called an
object) corresponds to a _pointer_ in C++.

This is what your program actually means, step by step:
int main()
{
c o; // creates an object (not a reference!) on the stack
{ // temporary scope for second statement in your code
c* anon = new c( 1 ); // create an anonymous object on the heap
o = *anon; // assign the _content_ of the anonymous object to o.
} // pointer to anonymous object is lost -> memory leak
o.print();
{ // temporary scope for 4th statement in your code
c* anon = new c( 2 ); // create an anonymous object on the heap
o = *anon; // assign the _content_ of the anonymous object to o.
} // pointer to anonymous object is lost -> memory leak
o.print();
}

Tobi

Lőrinczy Zsigmond

unread,
Feb 18, 2015, 3:29:18 AM2/18/15
to
Sorry for the bugs... A more complete version is here:
http://web.axelero.hu/lzsiga/ram.cc

example output:

constructor#1: user_data=1
constructor#2: copy source=#1, user_data=1
I am instance #2, user_data=1

constructor#3: user_data=2
user_data(operator=) to=#2, from=#3, user_data=2
I am instance #2, user_data=2
Message has been deleted

Martijn Lievaart

unread,
Feb 18, 2015, 9:45:11 AM2/18/15
to
On Wed, 18 Feb 2015 14:03:10 +0000, Stefan Ram wrote:

> Paavo Helde <myfir...@osa.pri.ee> writes:
>>other hand, you have two values (1 and 2), copy them over across
>>instances and print out with misleading messages, so there is no wonder
>
> So, in C++, an assignment of the kind
>
> object0 = object1
>
> can be thought of as a field-by-field copy (at least in the simple
> case of POD-like objects) from object1 to object0 that does not change
> the existence status of object0 or object1.

Correct, that is operator=() in action.

M4
Message has been deleted

Ian Collins

unread,
Feb 18, 2015, 11:37:40 PM2/18/15
to
Stefan Ram wrote:
>
> Actually, I wanted to observe how C++ interprets an example
> someone posted into the C newsgroup recently. Here is my
> attempt of a translation into C++:
>
> #include <iostream>
> #include <ostream>
> #include <memory>
>
> struct c /* this struct is as above (as before) */
> { int v;
> c( int const x ): v( x )
> { ::std::cout << "constructor of instance #" << v << ".\n"; }
> ~c(){ ::std::cout << "destructor of instance #" << v << ".\n"; }
> void print(){ ::std::cout << "I am instance #" << v << ".\n"; }};
>
> ::std::unique_ptr< c >f( ::std::unique_ptr< c >* p )
> { *p = ::std::make_unique< c >( 2 ); /* <- sequence point! (semicolon) */
> return ::std::make_unique< c >( 1 ); }
>
> int main()
> { ::std::unique_ptr< c >o( f( &o )); o->print(); }
>
> Does this program violate any rule of C++?

I'd pick passing the address of an uninitialised object (o) to f to be UB.

By the way, why to you keep adding superfluous scoping, but remove the
white-space that would make the code readable? I'm sure there are some
obscure corner cases that may justify the scoping, but Usenet posts
aren't one of them!

--
Ian Collins

Paavo Helde

unread,
Feb 18, 2015, 11:46:51 PM2/18/15
to
r...@zedat.fu-berlin.de (Stefan Ram) wrote in
news:initialization...@ram.dialup.fu-berlin.de:

> Paavo Helde <myfir...@osa.pri.ee> writes:
>>You can have an example with 2 instances (of class c) and no memory
>>leaks as well:
>>#include <iostream>
>>#include <ostream>
>>#include <memory>
>>struct c
>>{ int v;
>> c( int const x ): v( x )
>> { ::std::cout << "constructor of instance #" << v << ".\n"; }
>> ~c(){ ::std::cout << "destructor of instance #" << v << ".\n"; }
>> void print(){ ::std::cout << "I am instance #" << v << ".\n"; }};
>>int main()
>>{ std::unique_ptr<c> o(new c( 1 ));
>> o->print();
>> o = std::unique_ptr<c>(new c( 2 )); /* overwrite */
>> o->print(); }
>
> Actually, I wanted to observe how C++ interprets an example
> someone posted into the C newsgroup recently. Here is my
> attempt of a translation into C++:
>
> #include <iostream>
> #include <ostream>
> #include <memory>
>
> struct c /* this struct is as above (as before) */
> { int v;
> c( int const x ): v( x )
> { ::std::cout << "constructor of instance #" << v << ".\n"; }
> ~c(){ ::std::cout << "destructor of instance #" << v << ".\n"; }
> void print(){ ::std::cout << "I am instance #" << v << ".\n"; }};
>
>::std::unique_ptr< c >f( ::std::unique_ptr< c >* p )
> { *p = ::std::make_unique< c >( 2 ); /* <- sequence point! (semicolon)
> */
> return ::std::make_unique< c >( 1 ); }
>
> int main()
> { ::std::unique_ptr< c >o( f( &o )); o->print(); }
>
> Does this program violate any rule of C++?

I am pretty sure it does. The program takes the address of o before it is
constructed (this is OK in principle), and then goes on to assign
something to this location, still before the o object has been
constructed - this is definitely UB with a non-trivial class like
std::unique_ptr.

From the standard (12.7):

"For an object with a non-trivial constructor, referring to any non-
static member or base class of the object before the constructor begins
execution results in undefined behavior."

The crucial point here is that std::unique_ptr has a non-trivial
constructor. Note that in C the answer may be different because in C
there is no such thing as a non-trivial constructor.

Cheers
Paavo

StuartRedmann

unread,
Feb 20, 2015, 10:10:16 AM2/20/15
to
Am 18.02.15 um 15:03 schrieb Stefan Ram:
> Paavo Helde <myfir...@osa.pri.ee> writes:
>> other hand, you have two values (1 and 2), copy them over across
>> instances and print out with misleading messages, so there is no wonder
>
> So, in C++, an assignment of the kind
>
> object0 = object1
>
> can be thought of as a field-by-field copy (at least in the
> simple case of POD-like objects) from object1 to object0
> that does not change the existence status of object0 or object1.
>

That is the standard implementation of the copy constructor/operator=.
However, the intention of operator= is that it should behave in such a
way that two objects have the same behaviour after an assignment (at
least with respect to the problem domain).

I wonder, I wonder. Someone must have hacked into Stefan Ram's account.
I wouldn't have expected such a question from Stefan...
0 new messages