c++ - Destructor called without constructor bug revisited
- KarL (161/161) May 11 2003 charset="iso-8859-1"
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








"KarL" <someone somewhere.org>