Subject: RE: gcc bug - temporary objects not destroyed properly
From: Andrews Harold G Maj USAFA/DFCS <[email-masked]>
To: "'David Acton'" <[email-masked]>
Cc: "[email-masked]'" <[email-masked]>
Date: Tue, 9 Oct 2001 11:55:25 -0600

Mr. Acton,

I've seen something similar with gcc 2.95 under Cygwin.  The main difference
was that I had at least one virtual method in my class.  The compiler (using
the -Wall option) mentioned that I should declare a *virtual* destructor.
The one I created was was essentially empty, and seemed to fix the problem
(though I'm not completely certain why that was).  This might be something
that could fix your problem if you want to continue using GCC 2.95.x

BTW, I was not able to repeat the error using GCC 3.0.1 (though there were
some simple-to-fix compiler errors).  You might want to consider upgrading.

-Andy

================================================================
Harold G. "Andy" Andrews II, Ph.D., Major, USAF
Assistant Professor, Department of Computer Science
United States Air Force Academy
Room 1J-148 Fairchild Hall
DSN:  333-7553
Comm: (719) 333-7553
================================================================

-----Original Message-----
From: David Acton [mailto:[email-masked]] 
Sent: Tuesday, October 09, 2001 9:37 AM
To: [email-masked]
Subject: gcc bug - temporary objects not destroyed properly

Hi,

Here is a short test program to reproduce a bug we've discovered with gcc
2.95.3 targeted at Win32 (either cygwin or mingw).

The bug manifests itself as temporary objects which aren't destroyed. This
caused a large memory leak in our program.  We narrowed it down to a single
form of statement

    return _condition ? Example(2) + Example(3) : Example(4);
    //                  ^^^^^^^^^^  this is the object that doesn't get
destroyed!

It seems to need the return, the ? : and the creation of a temporary object
to trigger the bug.  Unfortunately our code had rather a lot of these
statements in!

Compile the test program with:

  gcc -Wall -pedantic cpp_bug.cc -o cpp_bug -lstdc++

It produces this when compiled native under linux
 
  Reading specs from /usr/local/lib/gcc-lib/i686-pc-linux-gnu/2.95.3/specs
  gcc version 2.95.3 20010315 (release)

  Testing...
  Creating object with value 42 at 0xbffffa10
  Creating object with value 3 at 0xbffff9b0
  Creating object with value 2 at 0xbffff9a0
  Creating object with value 2 at 0xbffff960 from another
  Creating object with value 5 at 0xbffffa00 from another
  result = 5

However when compiled under native Windows cygwin it produces

  Reading specs from /usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/specs
  gcc version 2.95.3-5 (cygwin special)

  Testing...
  Creating object with value 42 at 0x7afd1c
  Creating object with value 3 at 0x7afc94
  Creating object with value 2 at 0x7afc64
  Creating object with value 2 at 0x7afbb4 from another
  Creating object with value 5 at 0x7afcfc from another
  result = 5
  Oh no - 1 temporary object(s) did not have their destructors called!
   object at 0x7afc64

Likewise when compiled under a cross compiled mingw

  Reading specs from
/usr/local/cross/mingw/lib/gcc-lib/i386-mingw32msvc/2.95.2/specs
  gcc version 2.95.2 19991024 (release)

  Testing...
  Creating object with value 42 at 0259FDC8
  Creating object with value 3 at 0259FD40
  Creating object with value 2 at 0259FD10
  Creating object with value 2 at 0259FC60 from another
  Creating object with value 5 at 0259FDA8 from another
  result = 5
  Oh no - 1 temporary object(s) did not have their destructors called!
   object at 0259FD10

Likewise or under a newer mingw

  Reading specs from /usr/local/cross-tools/lib/gcc-lib/mingw32/2.95.3/specs
  gcc version 2.95.3 20010315 (release)

  Testing...
  Creating object with value 42 at 0081FDC8
  Creating object with value 3 at 0081FD40
  Creating object with value 2 at 0081FD10
  Creating object with value 2 at 0081FC60 from another
  Creating object with value 5 at 0081FDA8 from another
  result = 5
  Oh no - 1 temporary object(s) did not have their destructors called!
   object at 0081FD10

*/

//--------------------------------------------------------------------------
------

/* test compiler for obscure C++ memory leak! */

#include <stdio.h>
#include <list>

class Example;

static list<Example *> track;   // we keep track of all objects created and
destroyed using this list

class Example
{
public:
    Example(const int _value);
    Example(const Example & _a);
    ~Example();

    Example & operator+=(const Example & _a);

    const int get_value() const { return value; }

    Example test(const bool _condition);

private:
    int value;
};

Example::Example(const int _value)
{
    printf("Creating object with value %d at %p\n", _value, (void *)this);
    value = _value;
    track.push_back(this);
}

Example::Example(const Example & _a)
{
    printf("Creating object with value %d at %p from another\n",
_a.get_value(), (void *)this);
    value = _a.get_value();
    track.push_back(this);
}

Example::~Example()
{
    for (list<Example *>::iterator i = track.begin(); i != track.end(); ++i)
    {
	if ((*i) == this)
	{
	    track.erase(i);
	    return;
	}
    }

    printf("Oops! Cannot find object of value %d at %p in list\n", value,
(void *)this); }

// We define + and += operators for our object the Stroustrup way...

Example & Example::operator+=(const Example & _a)
{
    value += _a.get_value();
    return *this;
}

Example operator+(const Example & _a, const Example & _b)
{
    Example c(_a);

    c += _b;

    return c;
}

Example Example::test(const bool _condition)
{
    return _condition ? Example(2) + Example(3) : Example(4);

    //                  ^^^^^^^^^^  this is the object that doesn't get
destroyed!
}

int main (int argc, char *argv[])
{
    printf("Testing...\n");

    // Do our little test - put in a scope of its own so all the temporary
objects *should* get destroyed...

    {
	Example eg(42);
	printf("result = %d\n", eg.test(true).get_value());
    }

    if (!track.empty())
    {
	printf("Oh no - %d temporary object(s) did not have their
destructors called!\n", track.size());

	for (list<Example *>::const_iterator i = track.begin(); i !=
track.end(); ++i)
	{
	    printf(" object at %p\n", (void *)*i);
	}
    }
}

//--------------------------------------------------------------------------
------

My colleague Nick Craig-Wood was kind enough to try out the above with the
different compilers described and thus confirm the bug.

Hope this is of some help to you!

Cheers

-- 
David Acton
Alphawave Ltd

--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Bug reporting:         http://cygwin.com/bugs.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/

--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Bug reporting:         http://cygwin.com/bugs.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/