原文链接:http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=40
Last updated Jan 1, 2003.
For most programming tasks, the default implementation of new and delete is sufficient. In fact, many C++ programmers get along without even knowing that they can override these operators.
Yet, redefining these operators for a given class is essential in certain advanced programming tasks. One example might be when you need to ensure that all instances of a class are allocated from a custom memory pool instead of the free-store. (You can achieve something similar by usingplacement new, but that doesn't block users from using global new).
There are other useful applications of overriding new and delete: implementing a garbage collector, or ensuring that objects are allocated on a specific memory address (such as with a video card's internal memory buffer). In this section, I'll show how to override these operators, first on a per-class basis, and then globally.
Suppose you want to override new and delete for class A. Your first step is to declare these operators as member functions of this class:
#include <new> // for size_t class A { public: A(); ~A(); static void* operator new (size_t size); static void operator delete (void *p); };
When you declare new and delete as class members, they are implicitly declared static. Still, it's advisable to add the keyword static to their declarations to document this property explicitly.
The implementation of new is straightforward: it calls a custom allocation function, say,allocate_from_pool(), and returns the resulting pointer. You can use any other memory allocation function that suits your needs, such as malloc() or GlobalAlloc(), as long as that function meets the memory alignment requirement of your objects. Here's a typical implementation of new:
void* A::operator new (size_t size) { void *p=allocate_from_pool(size); return p; } // A's default ctor implicitly called here
Note that when this function exits, the class's default constructor executes automatically and constructs an object of type A. The matching version of delete looks as follows:
void A::operator delete (void *p) { release(p); // return memory to pool } // A's dtor implicitly called at this point
C++ guarantees that an object's destructor is automatically called just before delete executes. Therefore, you shouldn't invoke A's destructor explicitly (doing so would cause undefined behavior as the destructor will actually run twice).
Overriding new enables you to customize its functionality in various ways. For instance, you may add an error handling mechanism to the existing implementation by defining a special exception class mem_exception. An object of this class will be thrown in case of an allocation failure:
class mem_exception {}; void* A::operator new (size_t size) { void *p=allocate_from_pool(size); if (p==0) throw mem_exception(); return p; }
In a similar vein, you can extend delete's functionality to report the amount of available memory, write a message to a log file, and so on:
#include <ctime> // for time() void A::operator delete (void *p) { release(p); cout << "available pool size: " << get_pool_size(); write_to_log("deletion occurred at: ", time(0)); }
You use the overriding new and delete as you would use the built-in version of these operators; C++ automatically selects the overriding versions if they have been overridden in a specific class. Classes for which no overriding versions exist continue to use the built-in versions. For example
int main() { A *p=new A; // A::new delete p; // A::delete B *q=new B; // global built-in new delete q; // global built-in delete }
Until now, I've focused on a class-based override. What if you want override new and delete not just for one class, but for the entire application? C++ allows you to do that by defining global versions of these operators.
Unlike a class-based override, the global new and delete are declared in the global namespace. (Remember that you cannot declare these operators in a different namespace.) This technique may be useful if you use an older version of Visual C++ (versions 6.0 and below) and other slightly outdated compilers. The implementation of new in Visual C++ isn't standard compliant: new returnsNULL upon failure instead of throwing a std::bad_alloc exception, as required by the C++ ANSI/ISO standard. The following example overrides global new and delete to force standard compliant behavior:
#include <exception> // for std::bad_alloc #include <new> #include <cstdlib> // for malloc() and free() // Visual C++ fix of operator new void* operator new (size_t size) { void *p=malloc(size); if (p==0) // did malloc succeed? throw std::bad_alloc(); // ANSI/ISO compliant behavior return p; }
The matching delete looks as follows:
void operator delete (void *p) { free(p); }
Now you can use try and catch blocks to handle potential allocation exceptions properly, even if your compiler isn't yet fully-compliant in this regard:
int main() { try { int *p=new int[10000000]; // user-defined new //..use p delete p; // user-defined delete } catch (std::bad_alloc & x) { std::cout << x.what(); // display exception } }
Very few programming languages allow you to access and modify their inner-workings as does C++. Indeed, overriding new and delete is a very powerful feature: it gives you tighter control over the language's memory management policy and enables you to extend its functionality in various ways.
However, the built-in implementation of new and delete is suitable for most purposes. Don't override these operators unless you have compelling reasons to do so, and when doing so, remember always to override both of them.