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