www.digitalmars.com         C & C++   DMDScript  

c++ - Destructor called without constructor bug revisited

Content-Type: text/plain;
	charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable

I finally bought the WDJ CD-Rom and is able to locate the article on the =
old old bug:

This is the one which VC, BC and GCC no longer has the bug but DMC++ =
still has the bug.

WDDJ November 1995 (For bug fixing reference only, no copyright =
violation indented)

-------------------------------------------------------------------------=
-------

Last month's bug was something of an anomaly for this column. Instead of =
exploring a problem unique to Microsoft or Borland, I took a look at a =
C++ language feature that gave both vendors trouble. The placement =
syntax used with operator new had one set of implementation problems =
with Microsoft, and a slightly different set with Borland.

This month, I take a look at another problem that seems to have slipped =
past the test suites of both companies. Oddly enough, two different =
compilers generate equivalent buggy code!

C++ Class Member Functions
Member functions of C++ classes look and act much like the normal C =
functions I cut my teeth on long ago. In fact, C programmers can feel =
pretty comfortable with member functions if they view them as =
old-fashioned functions with a pair of superpowers:

  a.. Member functions get a secret argument not found in the parameter =
list. Referred to as this, it is a pointer to the object that was used =
to call the member function.=20
  b.. Member functions have access to protected and public data members =
of objects in their class (including this). This includes member =
functions in addition to data. Member functions can also use a shorthand =
syntax to access these data members, omitting explicit references to =
this. (e.g., using length instead of this->length).
Static Member Functions
This mental metaphor works pretty well for most member functions, but as =
usual, C++ has one more trick up its sleeve. In this case, the surprise =
is the static member function.

Static member functions give up one of the special attributes of member =
functions: they don't have the implicit this parameter. This means that =
you can call a static member function either with or without an object =
of the appropriate class. For example, if bar is a static member =
function of class foo, you can call it several different ways:

void test( foo *foo1 )
{
    foo foo2;
    foo1->bar();   //Called using an object pointer
    foo2.bar();    //Called using an object reference
    foo::bar();    //Called without an object

Static member functions are generally used to manage the environment of =
an entire class of objects. For example, in Hewlett Packard's release of =
the Standard Template Library, static member functions are used to =
manage memory pools for several container classes. Static member =
functions allocate big blocks of memory, then parcel out smaller pieces =
to individual members as necessary.

So what good is this capability? Static member functions have to give up =
the implicit use of an object. It would seem that static member =
functions could easily be replaced by global functions classified as =
friends of the given class.

While this is true, there are good reasons for using static member =
functions instead of global friend functions. Most important, static =
member functions help programmers adhere to the concept of =
encapsulation. If all the functions that modify objects of a given class =
are confined to the class definition, it becomes much easier to keep =
track of who does what. Encapsulation is one of the pillars that support =
object-oriented programming, and OOP, we are often told, is a good =
thing.

A Compiler Test for Static Member Functions
bug1195.cpp (Listing 1) shows a relatively short program that tests the =
ability of a compiler to properly use static member functions. The three =
subroutines, Test_1(), Test_2(), and Test_3() use three different =
techniques to call static member functions. Test_1() uses the most =
conventional technique, calling the function explicitly.

Test_2() and Test_3() call the static member function bar() by way of an =
object of class foo. The object in Test_2() is a local automatic =
variable; that in Test_3() is a temporary variable.

Class foo in bug1195.cpp contains some debug code that should be =
familiar to regular readers of this column. By printing out short =
messages when the object is created and destroyed, you can ensure that =
the compiler has generated the proper calls to constructors and =
destructors.

Sharp-eyed reader Aaron Margosis noticed that the output from this test =
program had a problem:

Test 1: bar
Test 2: ctor bar dtor
Test 3: bar dtor

The output from Test_3() indicates that the temporary foo object was =
destroyed without ever being properly constructed! This is clearly a =
major problem. Any moderately complex class is bound to corrupt data =
structures left and right when destroyed in this fashion.

Responses from Borland and Microsoft
The really odd part about this bug is that it is common to Borland C++ =
4.5 and Visual C++ 1.5. Seeing a bug such as this in a mature product is =
surprising enough, but I would never have expected to see it pop up in =
two completely different places at once.

Brian Myers from Borland acknowledged the bug and said it will be fixed =
in a future release. Presumably, this will be Borland C++ 5.0.

John Browne from Microsoft had this to say:

  This is fixed in our current product, Visual C++ 2.2.

Those of you using the 16-bit version of Visual C++ will presumably =
wonder if you can expect a fix as well.

Bug++ Rewards
Reader Margosis will be wearing his new WDJ t-shirt soon after this =
column sees print. You can join Aaron and several international =
supermodels on Mr. Blackwell's Best-Dressed list by sending us your =
favorite C++ bugs. Just write them up and post them to us at =
wdletter rdpub.com. With new releases of Borland's and Microsoft's =
compilers just around the corner, we're hoping for a full mailbox!=20

Mark Nelson is a programmer for Greenleaf Software in Dallas, Texas. =
Mark is the author of The C++ Programmer's Guide to the Standard =
Template Library, from IDG Books, as well as The Data Compression Book, =
from M&T Books. You can reach Mark on CompuServe at 73650,312.

Listing 1 bug1195.cpp - Problematic static member function call
//
//  This short program demonstrates a problem
//  found when calling a static member function.
//  For some reason, both VC 1.x and Borland 4.5
//  call the destructor for a temporary object that
//  was never constructed properly.  This error only
//  shows up in Test_3(), where a temporary object
//  is used to call a static member function.
//

#include <iostream.h>

class foo {
    public:
        foo() { cout << "ctor "; }
        foo( const &foo ) { cout << "copy "; }
        ~foo() { cout << "dtor "; }
        static void bar() { cout << "bar "; }
};

//
// Test 1 performs properly.  It doesn't construct
// or destroy any foo() objects.
//
void Test_1() {
    foo::bar();
}

//
// Test 2 works properly as well.  It constructs a
// single object, then destroys it when the function exits.
//
void Test_2() {
    foo f;
    f.bar();
}

//
// Test 3 has a problem.  It doesn't create the temporary
// object used here, but it destroys it!
//

void Test_3() {
    foo().bar();
}

void main()
{
    for ( int i =3D 1 ; i <=3D 3 ; i++ ) {
        cout << "Test " << i << ": ";
        switch ( i ) {
            case 1 : Test_1(); break;
            case 2 : Test_2(); break;
            case 3 : Test_3(); break;
        }
        cout << endl;
    }
}
May 11 2003