Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com
Answertopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions
Privacy Policy

  




 

 

Thinking in C++
Prev Contents / Index Next

Overloading new & delete for arrays

If you overload operator new and delete for a class, those operators are called whenever you create an object of that class. However, if you create an array of those class objects, the global operator new( ) is called to allocate enough storage for the array all at once, and the global operator delete( ) is called to release that storage. You can control the allocation of arrays of objects by overloading the special array versions of operator new[ ] and operator delete[ ] for the class. Here’s an example that shows when the two different versions are called:

//: C13:ArrayOperatorNew.cpp
// Operator new for arrays
#include <new> // Size_t definition
#include <fstream>
using namespace std;
ofstream trace("ArrayOperatorNew.out");

class Widget {
  enum { sz = 10 };
  int i[sz];
public:
  Widget() { trace << "*"; }
  ~Widget() { trace << "~"; }
  void* operator new(size_t sz) {
    trace << "Widget::new: "
         << sz << " bytes" << endl;
    return ::new char[sz];
  }
  void operator delete(void* p) {
    trace << "Widget::delete" << endl;
    ::delete []p;
  }
  void* operator new[](size_t sz) {
    trace << "Widget::new[]: "
         << sz << " bytes" << endl;
    return ::new char[sz];
  }
  void operator delete[](void* p) {
    trace << "Widget::delete[]" << endl;
    ::delete []p;
  }
};

int main() {
  trace << "new Widget" << endl;
  Widget* w = new Widget;
  trace << "\ndelete Widget" << endl;
  delete w;
  trace << "\nnew Widget[25]" << endl;
  Widget* wa = new Widget[25];
  trace << "\ndelete []Widget" << endl;
  delete []wa;
} ///:~

Here, the global versions of new and delete are called so the effect is the same as having no overloaded versions of new and delete except that trace information is added. Of course, you can use any memory allocation scheme you want in the overloaded new and delete.

You can see that the syntax of array new and delete is the same as for the individual object versions except for the addition of the brackets. In both cases you’re handed the size of the memory you must allocate. The size handed to the array version will be the size of the entire array. It’s worth keeping in mind that the only thing the overloaded operator new( ) is required to do is hand back a pointer to a large enough memory block. Although you may perform initialization on that memory, normally that’s the job of the constructor that will automatically be called for your memory by the compiler.

The constructor and destructor simply print out characters so you can see when they’ve been called. Here’s what the trace file looks like for one compiler:

new Widget
Widget::new: 40 bytes
*
delete Widget
~Widget::delete

new Widget[25]
Widget::new[]: 1004 bytes
*************************
delete []Widget
~~~~~~~~~~~~~~~~~~~~~~~~~Widget::delete[] 

Creating an individual object requires 40 bytes, as you might expect. (This machine uses four bytes for an int.) The operator new( ) is called, then the constructor (indicated by the *). In a complementary fashion, calling delete causes the destructor to be called, then the operator delete( ).

When an array of Widget objects is created, the array version of operator new( ) is used, as promised. But notice that the size requested is four more bytes than expected. This extra four bytes is where the system keeps information about the array, in particular, the number of objects in the array. That way, when you say

delete []Widget;

the brackets tell the compiler it’s an array of objects, so the compiler generates code to look for the number of objects in the array and to call the destructor that many times. You can see that, even though the array operator new( ) and operator delete( ) are only called once for the entire array chunk, the default constructor and destructor are called for each object in the array.

Thinking in C++
Prev Contents / Index Next

 
 
   Reproduced courtesy of Bruce Eckel, MindView, Inc. Design by Interspire