west@calgary.UUCP (Darrin West) (12/22/87)
A colleague of mine, Greg Lomow, walked into my office this morning and asked me an inspirational question. "If class b is derived from class a, and I create a new b, assigning it to a pointer of type a*, what happens when I delete the pointer? Will the space for the extension due to b be deleted?" I told him that it would call the destructor ~a(). malloc() or free() whatever will make sure the entire block of memory gets freed up. If you need to do something special to that part of the object associated with class b, you need to set up a virtual function that ~a() can call. This last statement (happily) is not true. YOU CAN DECLARE THE DESTRUCTOR FOR A TO BE VIRTUAL. I am very surprised that this is not stressed in the literature. I don't know how many times I wrote a special virtual function to delete elements from a generic (read: plain ole') linked list. If you do the following, then the derived destructor is called AUTOMATICALLY! struct q_el{ q_el *next; virtual ~q_el(); }; struct queue{ q_el *first; ~queue(); }; queue::~queue() { q_el *t = first; while(t!=NULL) delete t; } struct thing : public q_el{ lump *l; thing(){l = new lump;}; ~thing(){ delete l;}; }; And then make a nice queue of "things". When you delete the queue, all the "things" AND their "lumps" go away! I used to have to derive "queue" to a "thing_queue" and rewrite the destructor to specifically delete "things". Maybe everyone but me (I?) knows about this, but it was so neat I had to let every else know (if they didn't already). Things that work this good without being documented scare me. Is this a bug or a feature? -- Darrin West, Master's Unit (read: student). ..![ubc-vision,ihnp4]! Department of Computer Science alberta!calgary!west University of Calgary. Can you say '88 Winter Games? Brain fault (cortex dumped)
bs@alice.UUCP (12/23/87)
Yes virtual destructors are very nice. Yes they are part of the language (always were). Sorry for forgetting to mention them explicitly. I do mention them in my paper ``The Evolution of C++: 1985-1987'' for the USENIX C++ ``workshop'' in Santa Fe. The ``proceedings from that will be interesting reading.
lomow@calgary.UUCP (Greg Lomow) (01/20/88)
/* A friend of mine (Darrin West) posted a message asking about the use of virtual destructors. The answer was that the use of virtual destructors is legal. Having used them, I can atest to the fact that they are very useful. However, if you call delete AA when AA is NULL and the type of AA is "pointer to class a" where a's destructor is declared as virtual, a segmentation fault occurs. The C++ manual says that delete can be applied to any pointer, NULL or non-NULL. The example program shown below demonstrates this problem. If both pieces of code that are commented out are included in the program, a segmentation fault occurs. We conjecture that delete is trying to access the destructor for AA like C++ would access any other virtual routine; i.e., by accessing a table with pointers to virtual functions, however if AA is NULL then no such table exists. Is this a bug or are we using virtual destructors wrong? We are using version 1.2 of C++ on a Vax 11/780 running Unix 4.3. */ /* error if virtual dtor and delete AA ok if non-virtual dtor and delete AA ok if virtual dtor and delete AA commented out */ #include <stream.h> #define NULL 0 class a { public: a(); /* virtual*/ ~a(); }; a::a(){} a::~a(){ cout << "deleted\n";} main() { a *A = new a(); delete A; a *AA = NULL; // delete AA; } -- Greg Lomow lomow@cpsc.calgary.cdn or ....![ubc-vision,ihnp4]!alberta!calgary!lomow
pj@hrc63.co.uk (Mr P Johnson "Baddow") (02/28/89)
Stroustrup does not seem to deal with the question of destructing derived classes when all the calling function has is a pointer to the base class. By default only the base class destructor gets called. In Oregon C++ (for Suns) v 1.1, it is possible to declare the base destructor as virtual. This then ensures that the appropriate destructor is called for any derived classes provided that the _derived_class_has_a_destructor_. If the derived class has no explicit destructor then any member destructors will not be called (rather than being called implicitly as they would be normally). Does anyone know how other compilers behave? e.g. class Base { int foo; public: Base( ); virtual ~Base( ); // This is legal. }; class Derived_1: Base { Problem mung; // Destructor should be called implicitly public: Derived_1( ); // Constructor but no destructor }; class Derived_2: Base { Problem mung; // Destructor should be called implicitly public: Derived_2( ); // Constructor ~Derived_2( ); // and Destructor }; main( ) { Base *p1, *p2; p1 = new Derived_1; p2 = new Derived_2; // p1 & p2 both point to base classes of Derived_1/2. delete p1; // Will not call Problem::~Problem. delete p2; // Will call Problem::~Problem. }; // Paul Johnson.
mball@cod.NOSC.MIL (Michael S. Ball) (03/01/89)
In article <551@hrc63.co.uk> pj@hrc63.co.uk (Mr P Johnson "Baddow") writes: >In Oregon C++ (for Suns) v 1.1, it is possible to declare the base destructor >as virtual. This then ensures that the appropriate destructor is called for >any derived classes provided that the _derived_class_has_a_destructor_. If >the derived class has no explicit destructor then any member destructors will >not be called (rather than being called implicitly as they would be normally). I believe this is a bug. I will check on it and see that it gets handled if it is. This is the first time it has been reported, and it's not a well-defined area of the language. On logical grounds, though, it seems that if the base class destructor is virtual the one generated for the derived class should be as well. Thanks for pointing it out. If it turns out that the derived class shouldn't be virtual, I'll make another posting on the subject. Mike Ball TauMetric Corporation 1094 Cudahy Place., Ste 302 San Diego, CA 92110 mball@cod.nosc.mil
rfg@riunite.ACA.MCC.COM (Ron Guilmette) (03/02/89)
In article <1430@cod.NOSC.MIL> mball@cod.nosc.mil.UUCP (Michael S. Ball) writes: >In article <551@hrc63.co.uk> pj@hrc63.co.uk (Mr P Johnson "Baddow") writes: >>In Oregon C++ (for Suns) v 1.1, it is possible to declare the base destructor >>as virtual. This then ensures that the appropriate destructor is called for >>any derived classes provided that the _derived_class_has_a_destructor_. If >>the derived class has no explicit destructor then any member destructors will >>not be called (rather than being called implicitly as they would be normally). > >I believe this is a bug. I'm not sure what the "correct" semantics are myself, but I don't think it is a bug. I say that only because I have just recently been looking into the handling of "virtual" in GNU G++ (and in particular the handling of "virtual" for destructors) and I can tell you that G++ seems to do pretty much the same thing as described above. That is to say, if you have a base class which has an explicitly virtual destructor, then the destructor will get a entry in the vtable for that class. Also, any classes derived from such a class also (apparently) have entries for *their* destructors put into *their* vtables (in the same slot number). This seems to be true regardless of whether or not the destructors in the derived classes explicitly have the keyword "virtual" in their declarations. Thus, it seems that these destructors (in the derived classes) in effect become "implicitly" virtual. I also assume (but I have not yet checked) that (in G++) the destruction of an object which has either an explicitly or implicitly virtual destructor will be forced to go "indirect" through the vtable for that object. If that is true, then that means that the "appropriate destructor" should get called for all such objects regardless of whether or not the compiler can "statically" determine the actual class of an object which is being destroyed. // Ron Guilmette - MCC - Experimental (parallel) Systems Kit Project // 3500 West Balcones Center Drive, Austin, TX 78759 - (512)338-3740 // ARPA: rfg@mcc.com // UUCP: {rutgers,uunet,gatech,ames,pyramid}!cs.utexas.edu!pp!rfg -- // Ron Guilmette - MCC - Experimental (parallel) Systems Kit Project // 3500 West Balcones Center Drive, Austin, TX 78759 - (512)338-3740 // ARPA: rfg@mcc.com // UUCP: {rutgers,uunet,gatech,ames,pyramid}!cs.utexas.edu!pp!rfg
mball@cod.NOSC.MIL (Michael S. Ball) (03/03/89)
In article <101@riunite.ACA.MCC.COM> rfg@riunite.UUCP (Ron Guilmette) writes: >In article <1430@cod.NOSC.MIL> mball@cod.nosc.mil.UUCP (Michael S. Ball) writes: >>In article <551@hrc63.co.uk> pj@hrc63.co.uk (Mr P Johnson "Baddow") writes: >>>In Oregon C++ (for Suns) v 1.1, it is possible to declare the base destructor >>>as virtual. This then ensures that the appropriate destructor is called for >>>any derived classes provided that the _derived_class_has_a_destructor_. If >>>the derived class has no explicit destructor then any member destructors will >>>not be called (rather than being called implicitly as they would be normally). >> >>I believe this is a bug. > >I'm not sure what the "correct" semantics are myself, but I don't think it >is a bug. It is not a question of whether virtual destructors are allowed. They are, and should behave like all other virtual functions, which is to say that references to them will normally go through the virtual table. The problem, which is what I said was a bug, is that the destructors generated by the compiler were NOT being treated as virtual even though the base class destructor WAS virtual. Since a user destructor for the derived class will be virtual, it seems only logical that the compiler-generated one will be as well. It is not being so treated in the current version Oregon C++. I have no idea what G++ does in that situation, and the data you included does not cover this case. Mike Ball TauMetric Corporation 1094 Cudahy Pl. Ste 302 San Diego, CA 92110 (619)275-6381 mball@cod.nosc.mil
ejbjr@ihlpm.ATT.COM (Branagan) (03/04/89)
> I'm not sure what the "correct" semantics are myself, but I don't think it > is a bug. > > I say that only because I have just recently been looking into the handling > of "virtual" in GNU G++ (and in particular the handling of "virtual" for > destructors) and I can tell you that G++ seems to do pretty much the same > thing as described above. That is to say, if you have a base class which > has an explicitly virtual destructor, then the destructor will get a entry > in the vtable for that class. Also, any classes derived from such a class > also (apparently) have entries for *their* destructors put into *their* > vtables (in the same slot number). This seems to be true regardless of > whether or not the destructors in the derived classes explicitly have the > keyword "virtual" in their declarations. Thus, it seems that these > destructors (in the derived classes) in effect become "implicitly" > virtual. If a function (any function, including a destructor) is declared virtual in a base class, it will be virtual in all classes derived from that base class. I have not been able to find an explicit statement of this rule in Stroustrups's book (though it might be there somewhere), but some the examples make this behavior clear - for instance, check out the example in section 7.2.8 on virtual functions. Function `print' in class `employee' is declared as virtual; in class `manager', derived from `employee', `print' is not explicitly declared virtual. It certainly would not make sense for a non-virtual function to hide virtual functions - things just would not work right. The only other alternative would seem to be to generate an error message if a virtual function in a derived class was not explicitly declared virtual - something which is now out of the question for compatability reasons. I think we could use some better documentation about virtual destructors though - its not obvious from the documentation that a destructor can be declared virtual - BUT when should a destructor not be virtual? Can anyone give a reasonable example when the destructor for a derived class should not be called when an object from a base class is to be destroyed? Perhaps either all destructors should be automatically virtual (which has minor performance implications for classes with no derived classes), or the documentation should strongly advise making destructors virtual (or at least discuss virtual destructors and when/why they should be used). -- Jim Branagan (312) 416-7408 (work) (312) 810-0969 (home) Remember - Good planets are hard to find - Be kind to Mom.
ark@alice.UUCP (Andrew Koenig) (03/04/89)
In article <3130@ihlpm.ATT.COM>, ejbjr@ihlpm.ATT.COM (Branagan) writes: > I think we could use some better documentation about virtual > destructors though - its not obvious from the documentation that a > destructor can be declared virtual - BUT when should a destructor > not be virtual? Can anyone give a reasonable example when the > destructor for a derived class should not be called when an object > from a base class is to be destroyed? If you ever say `delete' to a pointer to a base class that actually points to an object of a derived class, the base class must have a virtual destructor. This rule is actually a slight oversimplification, but it's probably the right one to follow rather than trying to figure out the obscure (and possibly nonportable) exceptions. Why not make all destructors virtual? Performance. If there are no other virtual functions in the base class, making the destructor virtual adds a word of overhead per object. This can be substantial if you're defining little tiny objects. Moreover, C++ tries to impose overhead in only the cases that need it. In principle, once you've declared a virtual destructor in a base class, you do not need to redeclare it in any derived classes unless you need to say something there. However, some versions of C++ have a bug that causes incorrect code to be generated if there's a virtual destructor in the base class and no destructor at all in the derived class. Thus if you have a virtual destructor in a base class, define a destructor explicitly in all defined classes even if the destructor is empty. You don't have to say it's virtual in the derived classes. -- --Andrew Koenig ark@europa.att.com
richard@pantor.UUCP (Richard Sargent) (01/20/90)
We have a problem which seems to require virtual destructors. Is such a thing valid in C++? If not, is there an alternative? Stroustrup's book doesn't get into this very deeply, so I hope some one out there can advise us. The general description of our problem follows. Thanks for any and all help. We have a base class called FORM and a class derived from it called GROUP. The derived class provides a group of FORM's. It is supposed to support nested GROUP's. That is where the problem arises. If I delete an item from a GROUP which is a FORM, everything works as desired. But if the object to be deleted is itself a GROUP, we would like to delete it as a GROUP not as a FORM. Remember the GROUP class is a list of the base class (FORM). If virtual destructors are valid, then we should be able to have a simple and elegant solution. If they are not, then I need to know how else I can solve this problem. The overall structure of the solution described above has some extremely desirable properties for us, so we would like to retain it as much as possible. We have come up with one workaround, a virtual function "deleteyourself()", but that is not nearly as nice as having things handled correctly and automatically from the destructors. A completely transposed solution (along the lines of the Smalltalk container classes) is acceptable, but not nearly as nice. example (any syntax errors are the result of transcribing the text): #include <string.h> #include "gslist.h" // generic singly linked list, Stroustrup 7.3.2 typedef FORM* PFORM; ... struct FORM { char *id; FORM(char *name) { id = new char[strlen(name)+1]; strcpy(id, name); } // minor question: how well defined is the interaction between // the strdup() function and the above "new char" plus strcpy()? ... ~FORM() { if (id) delete id; } }; struct GROUP : FORM { gslist(PFORM) members; // group's member list GROUP(char *s) : (s) {} FORM *addmember(FORM *f) { members.append(f); return f; } GROUP *addmember(GROUP *g) { addmember((FORM *)g); return g; } ... ~GROUP() { PFORM fp; while (fp = members.get()) delete fp; } } ... main() { // // Create tree // // root // a1 // b1 // b1.1 // b1.2 // b1.2.1 // b1.2.2 // b1.3 // c1 // GROUP *root = new GROUP("root"); root->addmember( new FORM("a1") ); GROUP *b1 = root->addmember( new GROUP("b1") ); b1->addmember( new FORM("b1.1") ); GROUP *b1_2 = b1->addmember( new GROUP("b1.2") ); b1_2->addmember( new FORM("b1.2.1") ); b1_2->addmember( new FORM("b1.2.2") ); b1->addmember( new FORM("b1.3") ); root->addmember( new FORM("c1") ); delete b1; // Delete the subtree at b1. // This will not destroy b1.2.1 or b1.2.2 // because the base class destructor is used // to destroy b1.2 even though b1.2 is a group. } Richard Sargent Internet: richard@pantor.UUCP Systems Analyst UUCP: ...!mnetor!becker!pantor!richard
jeffa@hpmwtd.HP.COM (Jeff Aguilera) (01/23/90)
> We have a problem which seems to require virtual destructors. > Is such a thing valid in C++? Certainly. Didn't you even try class X { //... public: virtual ~X(); }; It won't (oops, it shouldn't, but probably will :-) break your compiler.