[comp.lang.c++] Bug in Borland C++ 2.0

murison@cfa.HARVARD.EDU (Marc A. Murison, RG) (03/10/91)

Dear NetFolks,

There seems to be a bug in the new Borland C++ 2.0 that was not
in Turbo C++ 1.0. When a temporary object is created, it is not
properly deleted when it goes out of scope. For example, suppose
we create a String object, then assign a character string to it:

/* ... */
    {
        String  A;          //default ctor called here
        A = "anteater";     //conversion ctor creates a temp. object here
    }

At this point (A goes out of scope), I *thought* there should've 
been two constructor calls and two corresponding destructor calls. 
In a test program (listed below for those who want to try it), bcc makes
two constructor calls and only one destructor call, whereas tcc
behaves as (I) expected. In examining the output (shown below), one
sees that the temporary object is not being deleted in the case of bcc.
I haven't tried this with the IDE compilers.

Is this a bug? Can someone please enlighten me?

Thanks,
Marc A. Murison
murison@cfacx2.harvard.edu


=======================================================================

Here is the output from the test program listed below:


output from BCC 2.0:
--------------------

startmem = 60544

    In default constructor:
    Address of this object is 0X104FFFEC

    In conversion constructor:  text = "anteater"
    Address of this object is 0X104FFFE8

    In operator =:  duplicating "anteater" to this->text
    Address of this object is   0X104FFFEC
    Address of copied object is 0X104FFFE8

After assignment: mem = 60512
Objects temp and A use 32 bytes.

    In destructor:  text = "anteater"
    Address of this object is 0X104FFFEC

endmem = 60528
Memory difference = 16 bytes
    


output from TCC 1.0:
--------------------

startmem = 60320

    In default constructor:
    Address of this object is 0X1252FFEC

    In conversion constructor:  text = "anteater"
    Address of this object is 0X1252FFE8

    In operator =:  duplicating "anteater" to this->text
    Address of this object is   0X1252FFEC
    Address of copied object is 0X1252FFE8

After assignment: mem = 60304
Objects temp and A use 16 bytes.

    In destructor:  text = "anteater"
    Address of this object is 0X1252FFE8

    In destructor:  text = "anteater"
    Address of this object is 0X1252FFEC

endmem = 60320
Memory difference = 0 bytes



----- snip ---- snip ----------- STRTEST.CPP ---------- snip ----- snip -----
/*
 *
 * test for bug in Borland BC++ 2.0
 *
 * Marc A. Murison
 * murison@cfacx2.harvard.edu
 *
 */

#include "str.hpp"
#include <alloc.h>

void main( void )
{
    unsigned startmem, endmem;
    startmem = coreleft();      //BC++ function to get available core
    cout << "\nstartmem = " << coreleft() << endl;

    //uppercase on hex output
    cout.flags(ios::uppercase);

    //dtor should be called twice as A goes out of scope in this block:
    {
        String  A;          //default ctor called here
        A = "anteater";     //conversion ctor creates a temp. object here
        cout << "\nAfter assignment: mem = " << coreleft();
        cout << "\nObjects temp and A use " << (startmem - coreleft()) 
             << " bytes." << endl;
    }

    endmem = coreleft();
    cout << "\nendmem = " << coreleft();
    cout << "\nMemory difference = " << (startmem - endmem) << " bytes"
         << endl;
}



----- snip ---- snip ----------- STR.HPP ---------- snip ----- snip -----
/*
 *
 * class String header file for use with STR.CPP and STRTEST.CPP
 *
 */

#include <string.h>
#include <iostream.h>
#pragma hdrstop

class String {

protected:
    char    *text;
    int     length;

public:
    String( void );
    String( const char *string );
    ~String( void );
    String& operator = ( const String &string );
};



----- snip ---- snip ----------- STR.CPP ---------- snip ----- snip -----
/*
 *
 * class String implementation for use with STRTEST.CPP
 *
 */
 
#include "str.hpp"

//
// default constructor
//
String::String( void )
{
    length = 0;
    text   = NULL;
    cout << "\n    In default constructor:";
    cout << "\n    Address of this object is " << (&(*this)) << endl;
}



//
// conversion constructor
//
String::String( const char *string )
{
    if( string == NULL ) {
        length = 0;
        text   = NULL;
    } else {
        length = strlen(string);
        text   = new char [length + 1];
        if( text == NULL )
            cerr << "\a\nMemory allocation error in conversion ctor\n";
        else
            strcpy( text, string );
    }

    if( text != NULL )
        cout << "\n    In conversion constructor:  text = \"" << text << "\"";
    else
        cout << "\n    In conversion constructor:  text = NULL";
    cout << "\n    Address of this object is " << (&(*this)) << endl;
}



//
// destructor
//
String::~String()
{
    if( text != NULL )
        cout << "\n    In destructor:  text = \"" << text << "\"";
    else
        cout << "\n    In destructor:  text = NULL";
    cout << "\n    Address of this object is " << (&(*this)) << endl;

    if( text != NULL )  delete text;
}



//
// assignment operator
//
String& String::operator = (const String &string )
{
    cout << "\n    In operator =:  duplicating \""
         << string.text << "\" to this->text";
    cout << "\n    Address of this object is   " << (&(*this));
    cout << "\n    Address of copied object is " << (&string) << endl;

    length = string.length;
    if( text != NULL )  delete text;
    text = new char [length + 1];
    if( text == NULL )
        cerr << "\a\nMemory allocation error in operator =\n";
    else
        strcpy( text, string.text );

    return *this;
}


----- snip ---- snip ----------- END ---------- snip ----- snip -----