mckenney@fir.itstd.sri.com (Paul E. McKenney) (03/20/90)
I would like to be able to maintain data structures containing pointers to objects that may have been created by different memory allocators, and would like the function that deletes these data structures to remain ignorant of all of the memory allocators. My first idea was to have a separate derived class for each type of memory allocator, and to make delete be a virtual operator, but this is of course not legal. My second idea is to make a virtual member function that invokes delete, as follows: class blat1 : public blat_base { public: virtual void delete_me() { delete this; } void operator delete(void *p) { dealloc_1(p); } } This seems to work under g++ version 1.36.4, at least as long as the programmer is careful not to deallocate a variable of class blat1 that was allocated statically or automatically. Is this sort of thing portable? Is there some more elegant way to express the concept of user-defined storage class in C++? Thanx, Paul
mckenney@sparkyfs.istc.sri.com (Paul Mckenney) (04/14/90)
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 <stream.h> 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.