//***************************** GCC bug demo example to show up a CTOR/DTOR imbalance ***************************** // // Type definitions and some (mostly!) inline code ... #include #include //---------------------------- // Our general base type 'Any' //---------------------------- class Any { public: // temporary, so my debug print can get to it... mutable int nUseCount; public: virtual char* mytostring() { static char* name = "(no-name)"; return name; } void incrementCounter() const {++nUseCount;} int decrementCounter() const {return (--nUseCount) == 0;} Any() : nUseCount(0) {}; // default ctor Any(const Any &) : nUseCount(0) {}; // 'copy ctor' - really just clears usecount virtual ~Any() {}; // dtor - virtual since all user classes will inherit from this }; //------------------------------------------------------------------------ // Domain and Range types to represent the 2 halfs of the map-element type //------------------------------------------------------------------------ class DomTypeBase : public Any { const char* str; public: DomTypeBase(const char* s) : str(s) {} }; class DomType : public DomTypeBase { int valuePt2; public: char* mytostring() { static char* name0 = "DomType"; return name0; } void testFunction() const { printf("\ntestFunction called"); fflush(NULL); } DomType(int val, const char* str) : DomTypeBase(str), valuePt2(val) {} }; class RanType { int value; public: RanType(int val) : value(val) {} }; //---------------------------------------------------------------- // Our reference-counted type 'Hndl' and its basetype 'BaseHandle' //---------------------------------------------------------------- class BaseHandle { protected: Any* Object; //---------------------------------------- // Concrete protected member functionality //---------------------------------------- // Incrememnt the sharing count void AddLink() { if (Object != NULL) Object->incrementCounter(); } // Decrement the sharing count void DelLink() { if (Object != NULL) { if (Object->decrementCounter()) { delete Object; } } } // Re-assign the object pointer. We must allow for the possibility that // the new object is the same as the old one. Either test for this // condition, or increment the new object pointer's reference count // before decrementing the old object's reference count (if we do it // the other way round, we risk de-allocating the object if they are // both the same). void assign(const Any* newObject) { if (newObject != NULL) { newObject->incrementCounter(); } DelLink(); Object = const_cast(newObject); } // Copy assignment operator. This is used by the system-generated copy // assignment operator of each derived class. void operator=(const BaseHandle& x) { assign(x.Object); } BaseHandle() : Object(NULL) {} // Copy ctor BaseHandle(const BaseHandle &bh) : Object(bh.Object) {}; // Construct from an object pointer explicit BaseHandle(const Any* ob) : Object(const_cast(ob)) { AddLink(); } // Destructor ~BaseHandle() { DelLink(); Object = NULL; } }; class Hndl : public BaseHandle { public : // Dereferencing operator, giving access to the object in a const way const DomType& operator*() const { return *(operator->()); } // Used for getting at the object to get at members in a 'const' way. const DomType* operator->() const { return static_cast(Object); } // Note: we allow the system to generate the copy constructor and the copy assignment operator // Assign a handle from an object pointer Hndl& operator=(const DomType* h) { assign(h); return *this; } // Default constructor, creates a 'null handle' Hndl() : BaseHandle() { } // Build a handle from an object pointer explicit Hndl(const DomType* xptr): BaseHandle(xptr) { } // Hand added copy CTOR to allow us to see when a handle gets copied .. Hndl(const Hndl & src) : BaseHandle(src.Object) { if(strcmp(src.Object->mytostring(), "DomType")==0) { printf("\n=====> Handle copy CTOR called to copy a '%s' (usecount = %d)!", src.Object->mytostring(), src.Object->nUseCount); fflush(NULL); //__asm__ //( "int $3" ); } } ~Hndl() { if(Object == NULL) printf("\nARRGGHHH!! Trying to destroy NULL object!"); else if(Object->nUseCount == 0) printf("\nERROR!!!! Too many destructors called!!!"); else printf("\nDestroying handle object OK"); } }; //---------------------------------- // Type to represent our map element //---------------------------------- class MapElement { public: Hndl dom; // reference-counted type RanType ran; MapElement(const Hndl d, const RanType r) : dom(d), ran(r) {} }; //----------------------------------- // Types to represent our stored node //----------------------------------- class Node : public Any { MapElement leaf; public: virtual MapElement operator [] (const int index) const { printf("\nIndexing operator (CONST) in 'Node' called!"); fflush(NULL); return leaf; } virtual MapElement & operator [] (const int index) { printf("\nIndexing operator (NON-const) in 'Node' called!"); fflush(NULL); return leaf; } explicit Node(const MapElement x) : leaf(x) {} }; //---------------------------------------------------------------- // Our 'From' type - is reference-counted, but this isn't relevant //---------------------------------------------------------------- class From : public BaseHandle { public: const Node& operator * () const { return *(operator -> ()); } const Node* operator -> () const { return static_cast(Object); } // CTOR - build a handle from an object pointer explicit From(const Node* d) : BaseHandle(d) {} }; //------------------------------------------------------------------------------------------------ // Type to act as the base for our test - just contains the function who's argument is in question //------------------------------------------------------------------------------------------------ class Sequence { public: void appendToMembers(Hndl newOne) const { newOne->testFunction(); // call a function on the reference-counted object ... } }; //--------------- // Our test class //--------------- class Test { public: void run() { const Sequence test = *(new Sequence());//test(Sequence()); int counter = 0; From members( From( new Node( MapElement( Hndl( new DomType(0, "zero")), RanType(1) ) ) ) ); printf("\nNow comes the crunch.."); #if !defined (WORKAROUND) test.appendToMembers( (*members)[counter].dom ); // <------------ THIS IS THE PROBLEMATIC LINE OF CODE #else // the fix.. Hndl tralala = (*members)[counter].dom; printf("\nNow comes the crunch (2).."); test.appendToMembers( tralala ); // Same call, but no operator calls here #endif printf("\nCrunch over!"); } }; //**** The test itself **** int main(int argc, char **argv) { Test ourTest; ourTest.run(); printf("\n\n"); return 0; } // End.