home *** CD-ROM | disk | FTP | other *** search
- Path: sparky!uunet!haven.umd.edu!darwin.sura.net!zaphod.mps.ohio-state.edu!sdd.hp.com!elroy.jpl.nasa.gov!swrinde!news.dell.com!math.utexas.edu!ut-emx!jamshid
- From: jamshid@ut-emx.uucp (Jamshid Afshar)
- Newsgroups: comp.lang.c++
- Subject: Re: help?? is their a method for solving dynamic allocation problemes in c++
- Summary: example of fairly painless operator new/delete overloading
- Keywords: overloading, new, delete, templates
- Message-ID: <78861@ut-emx.uucp>
- Date: 1 Sep 92 19:11:24 GMT
- References: <1992Aug19.203838.20501@bmw.mayo.edu> <78304@ut-emx.uucp>
- Reply-To: jamshid@emx.utexas.edu
- Organization: The University of Texas at Austin; Austin, Texas
- Lines: 213
-
- In article <1992Aug19.203838.20501@bmw.mayo.edu> staniszewski@mayo.edu writes
- >The problem I am looking at is to trackdown where my dynamic memory
- >problems are using new and delete. I have a nice set of replacement
- >routines for the alloc routines [...but it...] seems possible to do this
- >for new, since the global operator can be overloaded, but this is a little
- >awkward. AND the global delete cannot be overloaded PERIOD.
-
- I previously replied to this article incorrectly. I thought Stan
- just wanted to override his compiler's global
- void* operator new(size_t)
- and
- void operator delete(void*)
- functions since it's common to replace them with a debugging
- version. Actually, he wrote me that he truely meant "overload" (as
- in using operator new with a different syntax -- the "awkward" part).
- He also wrote that on one system he works with (VMS?) the global
- operator delete() cannot be replaced anyway because it is in a DLL.
- Is this true -- are there any workarounds? Will ANSI C++ *require*
- that implementations allow the replacement of these functions? Anyway,
- back to the original question...
-
- First, remember that general use of an overloaded new is not only a
- pain to type but dangerous because you must be sure that you do not
- delete using the regular delete operator. Since deletion is not
- always done in the same function or part of the code, and in fact may
- be done in multiple places, it wasn't felt that allowing the
- overloading of the delete operator would buy much over requiring
- deletion by a user-defined function (read ARM 5.3.4).
-
- So, assuming you want to go through with this, the syntax is bearable
- if your compiler supports templates. Let's say you had a memory
- package which also logged data such as the file/line where the
- new/delete was being performed:
-
- void* operator new(size_t size, char* filename, int line);
- void /*operator*/ delete_(void* p, char* filename, int line);
- // can't *really* overload operator delete()
-
- To new an object you simply do:
-
- Foo* p = new (__FILE__,__LINE__) Foo; // instead of 'Foo* p = new Foo;'
-
- Deleting is harder because you must explicitly call the destructor.
- A function template helps alot.
-
- template<class T> inline
- void mydelete(T* p, char* filename, int line) {
- p->~T(); // BC++ 3.1 requires p->T::~T() for built-in types!
- delete_((void*)p, filename, line); // return bytes to free store
- }
- ...
- mydelete(p, __FILE__, __LINE__); // instead of 'delete p;'
-
- All this is easy compared to what you have to go through with arrays
- (and I haven't even considered multi-dim arrays). You must
- explicitly store the size of the array so you know how many
- destructors to call. Normally (and if you only needed to overload
- the new operator) this is handled by the implementation. In my
- example I'll store the size at the beginning of the allocated
- block. So instead of allocating a T[n], I will allocate a
- variable-sized ArrayBlock struct:
-
- template<class T> // amount actually allocated for array
- class ArrayBlock {
- size_t num; // actual number of elements in arr
- T arr[1]; // actual size will be 'num'
- public:
- static T* new_arr(size_t num, char* filename, int line);
- static void delete_arr(T* arr, char* filename, int line);
- };
-
- The static member functions actually allocate and deallocate the
- ArrayBlock. They handle the storing of the size, constructing and
- destroying the elements, and adjusting the 'T*' to/from an
- 'ArrayBlock<T>*'. A helper function:
-
- template<class T> inline
- void delete_arr(T* arr, char* filename, int line)
- { ArrayBlock<T>::delete_arr(arr, filename, line); }
-
- does the deletion so you don't have to specify the type. Your code
- will therefore look like:
-
- Foo* p = ArrayBlock<Foo>::new_arr(10, __FILE__, __LINE__);
- // instead of 'Foo* p = new Foo[10];'
- ...
- delete_arr(p); // instead of 'delete [] p;'
-
- Any questions? I'm appending the complete code which uses macros to
- make the syntax more familiar (though I'm not sure I would recommend
- using them for this same reason). I haven't done much testing beyond
- compiling it with BC++ 3.1. It probably doesn't compile under BC++
- 3.0. Cfront 3.x and gcc 2.x should work, though they may have bugs
- requiring code changes. Let me know if I've overlooked anything.
-
- Jamshid Afshar
- jamshid@emx.utexas.edu
-
- ////---cut here---////
- #include <stddef.h> // for size_t, offsetof()
-
- /***** these two functions do the debugging/statistics gathering *****/
-
- void* operator new(size_t size, char* filename, int line);
- void delete_(void* p, char* filename, int line);
-
- template<class T> // helper for DELETE
- inline void mydelete(T* p, char* filename, int line) {
- #ifndef __BORLANDC__
- p->~T();
- #else // Borland 3.1 compiler bug (very bad if ~T() virtual)
- p->T::~T();
- #endif
- delete_((void*)p, filename, line);
- }
-
- /***** use these two macros in your code to allocate single objects *****/
-
- #define NEW(TYPE) new (__FILE__,__LINE__) TYPE
- #define DELETE(p) mydelete(p, __FILE__, __LINE__)
-
-
- /***** new/delete arrays is trickier *****/
-
- // might be defined in your <new.h>, but I don't think it's supposed to be
- inline void* operator new(size_t, void* p) { return p; }
-
- template<class T>
- class ArrayBlock {
- size_t num; // number of elements in array has to be stored for delete
- T arr[1]; // actually could be more elements in array
- public:
- static T* new_arr(size_t num, char* filename, int line) {
- ArrayBlock<T>* block = (ArrayBlock<T>*) operator
- new(sizeof(ArrayBlock<T>)+sizeof(T)*(num-1), filename, line);
- block->num = num;
- // now construct each element using default ctor
- for (int i=0; i<num; i++)
- new ((void*)(&block->arr[i])) T;
- return block->arr;
- }
-
- static void delete_arr(T* arr, char* filename, int line) {
- if (!arr) return;
- ArrayBlock<T>* block = (ArrayBlock<T>*)((char*)arr-offsetof(ArrayBlock<T>, arr));
- size_t num = block->num;
- while (num>0)
- #ifndef __BORLANDC__
- arr[--num].~T(); // destroy each element in reverse order
- #else // Borland 3.1 compiler bug
- (&arr[--num])->T::~T();
- #endif
- delete_((void*)block, filename, line);
- }
- };
-
- template<class T> inline
- void delete_arr(T* arr, char* filename, int line)
- { ArrayBlock<T>::delete_arr(arr, filename, line); }
-
- /***** use these two macros in your code to allocate array of objects *****/
-
- #define NEWARR(TYPE, NUM) ArrayBlock<TYPE>::new_arr(NUM, __FILE__, __LINE__)
- #define DELETEARR(p) delete_arr(p, __FILE__, __LINE__)
-
-
- /***** These are the functions you provide *****/
-
- #include <iostream.h>
- #include <stdlib.h>
-
- void* operator new(size_t size, char* filename, int line) {
- cout << "new(" << size << ", " << filename << ", " << line << ")\n";
- return malloc(size);
- }
-
- void delete_(void* p, char* filename, int line) {
- cout << "free(" << p << ", " << filename << ", " << line << ")\n";
- free(p);
- }
-
-
- /***** This is your code *****/
-
- #include <iostream.h>
-
- class Foo { int i; public: Foo(); ~Foo(); };
-
- Foo::Foo() {
- cout << "Foo()\n";
- }
-
- Foo::~Foo() {
- cout << "~Foo()\n";
- }
-
- int main() {
- char* p = NEW(char); // char* p = new char;
- char* arr = NEWARR(char,8); // char* arr = new char[8];
- DELETE(p); // delete p;
- DELETEARR(arr); // delete [] arr;
-
- cout << "--------------\n\n";
-
- int n = 8;
- Foo* foop = NEW(Foo); // Foo* p = new Foo;
- Foo* fooarr = NEWARR(Foo,n); // Foo* arr = new Foo[8];
- DELETE(foop); // delete p;
- DELETEARR(fooarr); // delete [] arr;
-
- return 0;
- }
- ////---cut here---////
-