home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #19 / NN_1992_19.iso / spool / comp / lang / cplus / 13156 < prev    next >
Encoding:
Text File  |  1992-09-01  |  8.2 KB  |  227 lines

  1. 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
  2. From: jamshid@ut-emx.uucp (Jamshid Afshar)
  3. Newsgroups: comp.lang.c++
  4. Subject: Re: help?? is their a method for solving dynamic allocation problemes in c++
  5. Summary: example of fairly painless operator new/delete overloading
  6. Keywords: overloading, new, delete, templates
  7. Message-ID: <78861@ut-emx.uucp>
  8. Date: 1 Sep 92 19:11:24 GMT
  9. References: <1992Aug19.203838.20501@bmw.mayo.edu> <78304@ut-emx.uucp>
  10. Reply-To: jamshid@emx.utexas.edu
  11. Organization: The University of Texas at Austin; Austin, Texas
  12. Lines: 213
  13.  
  14. In article <1992Aug19.203838.20501@bmw.mayo.edu> staniszewski@mayo.edu writes
  15. >The problem I am looking at is to trackdown where my dynamic memory
  16. >problems are using new and delete. I have a nice set of replacement
  17. >routines for the alloc routines [...but it...] seems possible to do this
  18. >for new, since the global operator can be overloaded, but this is a little
  19. >awkward.  AND the global delete cannot be overloaded PERIOD.
  20.  
  21. I previously replied to this article incorrectly.  I thought Stan
  22. just wanted to override his compiler's global
  23.    void* operator new(size_t)
  24. and
  25.    void operator delete(void*)
  26. functions since it's common to replace them with a debugging
  27. version.  Actually, he wrote me that he truely meant "overload" (as
  28. in using operator new with a different syntax -- the "awkward" part).
  29. He also wrote that on one system he works with (VMS?) the global
  30. operator delete() cannot be replaced anyway because it is in a DLL.
  31. Is this true -- are there any workarounds?  Will ANSI C++ *require*
  32. that implementations allow the replacement of these functions?  Anyway,
  33. back to the original question...
  34.  
  35. First, remember that general use of an overloaded new is not only a
  36. pain to type but dangerous because you must be sure that you do not
  37. delete using the regular delete operator.  Since deletion is not
  38. always done in the same function or part of the code, and in fact may
  39. be done in multiple places, it wasn't felt that allowing the
  40. overloading of the delete operator would buy much over requiring
  41. deletion by a user-defined function (read ARM 5.3.4).
  42.  
  43. So, assuming you want to go through with this, the syntax is bearable
  44. if your compiler supports templates.  Let's say you had a memory
  45. package which also logged data such as the file/line where the
  46. new/delete was being performed:
  47.  
  48.    void* operator new(size_t size, char* filename, int line);
  49.    void /*operator*/ delete_(void* p, char* filename, int line);
  50.          // can't *really* overload operator delete()
  51.  
  52. To new an object you simply do:
  53.  
  54.    Foo* p = new (__FILE__,__LINE__) Foo;  // instead of 'Foo* p = new Foo;'
  55.  
  56. Deleting is harder because you must explicitly call the destructor.
  57. A function template helps alot.
  58.  
  59.    template<class T> inline
  60.    void mydelete(T* p, char* filename, int line) {
  61.       p->~T();    // BC++ 3.1 requires p->T::~T() for built-in types!
  62.       delete_((void*)p, filename, line);     // return bytes to free store
  63.    }
  64.    ...
  65.    mydelete(p, __FILE__, __LINE__);    // instead of 'delete p;'
  66.  
  67. All this is easy compared to what you have to go through with arrays
  68. (and I haven't even considered multi-dim arrays).  You must
  69. explicitly store the size of the array so you know how many
  70. destructors to call.  Normally (and if you only needed to overload
  71. the new operator) this is handled by the implementation.  In my
  72. example I'll store the size at the beginning of the allocated
  73. block.  So instead of allocating a T[n], I will allocate a
  74. variable-sized ArrayBlock struct:
  75.  
  76.    template<class T>    // amount actually allocated for array
  77.    class ArrayBlock {
  78.       size_t num;   // actual number of elements in arr
  79.       T arr[1];     // actual size will be 'num'
  80.    public:
  81.       static T* new_arr(size_t num, char* filename, int line);
  82.       static void delete_arr(T* arr, char* filename, int line);
  83.    };
  84.  
  85. The static member functions actually allocate and deallocate the
  86. ArrayBlock.  They handle the storing of the size, constructing and
  87. destroying the elements, and adjusting the 'T*' to/from an
  88. 'ArrayBlock<T>*'.  A helper function:
  89.  
  90.    template<class T> inline
  91.    void delete_arr(T* arr, char* filename, int line)
  92.       { ArrayBlock<T>::delete_arr(arr, filename, line); }
  93.  
  94. does the deletion so you don't have to specify the type.  Your code
  95. will therefore look like:
  96.  
  97.    Foo* p = ArrayBlock<Foo>::new_arr(10, __FILE__, __LINE__);
  98.             // instead of 'Foo* p = new Foo[10];'
  99.    ...
  100.    delete_arr(p);    // instead of 'delete [] p;'
  101.  
  102. Any questions?  I'm appending the complete code which uses macros to
  103. make the syntax more familiar (though I'm not sure I would recommend
  104. using them for this same reason).  I haven't done much testing beyond
  105. compiling it with BC++ 3.1.  It probably doesn't compile under BC++
  106. 3.0.  Cfront 3.x and gcc 2.x should work, though they may have bugs
  107. requiring code changes.  Let me know if I've overlooked anything.
  108.  
  109. Jamshid Afshar
  110. jamshid@emx.utexas.edu
  111.  
  112. ////---cut here---////
  113. #include <stddef.h>     // for size_t, offsetof()
  114.  
  115. /***** these two functions do the debugging/statistics gathering *****/
  116.  
  117. void* operator new(size_t size, char* filename, int line);
  118. void delete_(void* p, char* filename, int line);
  119.  
  120. template<class T>    // helper for DELETE
  121. inline void mydelete(T* p, char* filename, int line) {
  122. #ifndef __BORLANDC__
  123.    p->~T();
  124. #else    // Borland 3.1 compiler bug (very bad if ~T() virtual)
  125.    p->T::~T();
  126. #endif
  127.    delete_((void*)p, filename, line);
  128. }
  129.  
  130. /***** use these two macros in your code to allocate single objects *****/
  131.  
  132. #define NEW(TYPE) new (__FILE__,__LINE__) TYPE
  133. #define DELETE(p) mydelete(p, __FILE__, __LINE__)
  134.  
  135.  
  136. /***** new/delete arrays is trickier *****/
  137.  
  138. // might be defined in your <new.h>, but I don't think it's supposed to be
  139. inline void* operator new(size_t, void* p) { return p; }
  140.  
  141. template<class T>
  142. class ArrayBlock {
  143.    size_t num;   // number of elements in array has to be stored for delete
  144.    T arr[1];     // actually could be more elements in array
  145. public:
  146.    static T* new_arr(size_t num, char* filename, int line) {
  147.       ArrayBlock<T>* block = (ArrayBlock<T>*) operator
  148.                new(sizeof(ArrayBlock<T>)+sizeof(T)*(num-1), filename, line);
  149.       block->num = num;
  150.       // now construct each element using default ctor
  151.       for (int i=0; i<num; i++)
  152.          new ((void*)(&block->arr[i])) T;
  153.       return block->arr;
  154.    }
  155.  
  156.    static void delete_arr(T* arr, char* filename, int line) {
  157.       if (!arr) return;
  158.       ArrayBlock<T>* block = (ArrayBlock<T>*)((char*)arr-offsetof(ArrayBlock<T>, arr));
  159.       size_t num = block->num;
  160.       while (num>0)
  161. #ifndef __BORLANDC__
  162.          arr[--num].~T();   // destroy each element in reverse order
  163. #else  // Borland 3.1 compiler bug
  164.          (&arr[--num])->T::~T();
  165. #endif
  166.       delete_((void*)block, filename, line);
  167.    }
  168. };
  169.  
  170. template<class T> inline
  171. void delete_arr(T* arr, char* filename, int line)
  172.    { ArrayBlock<T>::delete_arr(arr, filename, line); }
  173.  
  174. /***** use these two macros in your code to allocate array of objects *****/
  175.  
  176. #define NEWARR(TYPE, NUM) ArrayBlock<TYPE>::new_arr(NUM, __FILE__, __LINE__)
  177. #define DELETEARR(p) delete_arr(p, __FILE__, __LINE__)
  178.  
  179.  
  180. /***** These are the functions you provide *****/
  181.  
  182. #include <iostream.h>
  183. #include <stdlib.h>
  184.  
  185. void* operator new(size_t size, char* filename, int line) {
  186.    cout << "new(" << size << ", " << filename << ", " << line << ")\n";
  187.    return malloc(size);
  188. }
  189.  
  190. void delete_(void* p, char* filename, int line) {
  191.    cout << "free(" << p << ", " << filename << ", " << line << ")\n";
  192.    free(p);
  193.    }
  194.  
  195.  
  196. /***** This is your code *****/
  197.  
  198. #include <iostream.h>
  199.  
  200. class Foo { int i; public: Foo(); ~Foo(); };
  201.  
  202. Foo::Foo() {
  203.    cout << "Foo()\n";
  204. }
  205.  
  206. Foo::~Foo() {
  207.    cout << "~Foo()\n";
  208. }
  209.  
  210. int main() {
  211.    char* p = NEW(char);          // char* p = new char;
  212.    char* arr = NEWARR(char,8);   // char* arr = new char[8];
  213.    DELETE(p);                    // delete p;
  214.    DELETEARR(arr);               // delete [] arr;
  215.  
  216.    cout << "--------------\n\n";
  217.  
  218.    int n = 8;
  219.    Foo* foop = NEW(Foo);         // Foo* p = new Foo;
  220.    Foo* fooarr = NEWARR(Foo,n);  // Foo* arr = new Foo[8];
  221.    DELETE(foop);                 // delete p;
  222.    DELETEARR(fooarr);            // delete [] arr;
  223.  
  224.    return 0;
  225. }
  226. ////---cut here---////
  227.