Path: utzoo!utgpu!news-server.csri.toronto.edu!clyde.concordia.ca!uunet!tut.cis.ohio-state.edu!zaphod.mps.ohio-state.edu!uakari.primate.wisc.edu!ames!sparkyfs!mckenney From: mckenney@sparkyfs.istc.sri.com (Paul Mckenney) Newsgroups: comp.lang.c++ Subject: Re: Should ``delete this;'' appear in virtual member functions? Message-ID: <30961@sparkyfs.istc.sri.com> Date: 14 Apr 90 01:53:02 GMT References: <1990Mar31.183444.5839@Solbourne.COM> <30755@sparkyfs.istc.sri.com> Reply-To: mckenney@itstd.sri.com (Paul E. McKenney) Organization: SRI International, Menlo Park, CA 94025 Lines: 214 This note describes why I want to be able to use ``delete this;'' in virtual member functions and includes a small program that uses this trick in the way that I intend to. This is a summary of what I have learned in playing with this problem and in discussions with a number of people, including James Blasius, Gary Aitken, Scott Turner, Scott Bruns, Robert Selinger, Andrew Koenig, Michael Tiemann, and Bjarne Stroustrup. I want to be able to have multiple high-speed free pools for a given base class (call it ``buf''). I need high speed because buf is allocated and deallocated frequently, and I need multiple pools because there are several entities competing for bufs; by assigning each its own pool, I prevent starvation. However, the bufs are deallocated by code that cannot know which pool a given buf belongs to until runtime. I do -not- want this code to have to know explicitly about all the different pools because I do not want to have to modify this code each time I add a new pool. Furthermore, I want to prevent a naive programmer from attempting to free a statically allocated variable (auto, static, or extern). This is accomplished by defining a virtual member function as follows: virtual void del() { delete this; } (I have adopted Gary Aitken's choice of name, ``del'' being easier to type than the ``delete_me'' that I used in my original posting. :-) If ``p'' is a pointer to a base class, and if there is a separate derived class for each storage pool, then: p->del(); will return the object pointed to by ``p'' to the proper pool. Note that each derived class must redefine ``operator delete'' (and, of course, ``operator new'') to handle the storage pool for that derived class. Finally, the destructors for all of the classes are protected. This means that the destructor can be invoked only from a member or friend function, which means that any attempt to define an instance of any of the classes as a variable will get a compiler error. This is because C++ invokes the destructor for each variable when that variable goes out of scope. Since the destructor is private, it cannot be accessed from unprivileged code. This means that the user has to do something creative (such as using casts) in order to break ``del''. Thanx, Paul PS. This code is followed by the output of a sample run, a line of dashes separates code from output. ---------------------------------------------------------------------------- #include class buf { public: buf() {}; virtual void del() = 0; /* Delete this object. */ protected: virtual ~buf() {}; /* Prevent a variable of type */ /* buf from being defined. */ }; class buf1 : public buf { public: buf1() {}; virtual void del(); /* Delete this object. */ static void *operator new(size_t s); protected: virtual ~buf1() {}; /* Prevent a variable of type */ /* buf from being defined. */ static void operator delete(void *p); class buf1 *next; static class buf1 *free; }; void buf1::del() { delete this; } static void *buf1::operator new(size_t s) { class buf1 *p; if (buf1::free == NULL) { p = ::new buf1; cout << "buf1::" << (int)p << " allocating new element.\n"; return ((void *)p); } else { p = buf1::free; cout << "buf1::" << (int)p << " reallocating old element.\n"; buf1::free = p->next; return ((void *)p); } } static void buf1::operator delete(void *p) { class buf1 *p1 = (class buf1 *)p; cout << "buf1::" << (int)p << " freeing element.\n"; p1->next = p1->free; p1->free = p1; } static class buf1 *buf1::free = NULL; class buf2 : public buf { public: buf2() {}; virtual void del(); /* Delete this object. */ static void *operator new(size_t s); protected: virtual ~buf2() {}; /* Prevent a variable of type */ /* buf from being defined. */ static void operator delete(void *p); class buf2 *next; static class buf2 *free; }; void buf2::del() { delete this; } static void *buf2::operator new(size_t s) { class buf2 *p; if (buf2::free == NULL) { p = ::new buf2; cout << "buf2::" << (int)p << " allocating new element.\n"; return ((void *)p); } else { p = buf2::free; cout << "buf2::" << (int)p << " reallocating old element.\n"; buf2::free = p->next; return ((void *)p); } } static void buf2::operator delete(void *p) { class buf2 *p1 = (class buf2 *)p; cout << "buf2::" << (int)p << " freeing element.\n"; p1->next = p1->free; p1->free = p1; } static class buf2 *buf2::free = NULL; main() { class buf1 *p1 = new buf1; // class buf1 bfr2; // gets compiler error, this prevents // attempts to delete a variable. class buf *p2; cout << "p1->del();\n"; p1->del(); cout << "p1 = new buf1;\n"; p1 = new buf1; cout << "p2 = new buf1;\n"; p2 = new buf1; cout << "p2->del();\n"; p2->del(); cout << "p2 = new buf2;\n"; p2 = new buf2; cout << "p2->del();\n"; p2->del(); cout << "p1->del();\n"; p1->del(); } ------------------------------------------------------------------------ buf1::138880 allocating new element. p1->del(); buf1::138880 freeing element. p1 = new buf1; buf1::138880 reallocating old element. p2 = new buf1; buf1::139160 allocating new element. p2->del(); buf1::139160 freeing element. p2 = new buf2; buf2::139256 allocating new element. p2->del(); buf2::139256 freeing element. p1->del(); buf1::138880 freeing element.