|
 |
 |
[an error occurred while processing this directive]
Steps
For this example, a simple memory allocation counter will be implementedit will keep track of the total bytes allocated, deleted, and the number of calls to new and delete operators. You might be surprised at the execution results because allocating an array of these objects does not show in the statistics.
- 1. Create a class that contains statistics or information you want to keep track of. In this case, it will be just bytes allocated, de-allocated, and the number of calls to new and delete. These are by no means the only things that can be dealt with this way. The following code shows the class to keep memory statistics:
class MemStats
{
private:
unsigned BytesAllocated ;
unsigned BytesDeleted ;
unsigned CallsToNew ;
unsigned CallsToDel ;
fstream StatsFile;
public: MemStats() : BytesAllocated(0), BytesDeleted(0),
⇒CallsToNew(0), CallsToDel(0) {
StatsFile.open( memstats.txt, ios::out ) ; if( !StatsFile )
throw runtime_error( Could not open memstats.txt ) ;
}
MemStats( const string& filename ) : BytesAllocated(0),
⇒BytesDeleted(0), CallsToNew(0), CallsToDel(0)
{
StatsFile.open( filename.c_str() , ios::out ) ;
if( !StatsFile )
throw runtime_error( Could not open user-defined
⇒file. ) ;
}
// these functions can be expanded to do all sorts of other
// things, like print more info into the output file, time
// and date, etc. or keep track of the allocated chunks of
// memory in an associative array.
void newcalled( size_t size )
{
CallsToNew++ ;
BytesAllocated += size ;
}
void delcalled( size_t size )
{
CallsToDel++ ;
BytesDeleted += size ;
}
~MemStats()
{
StatsFile << endl
<< Memory allocation statistics : << endl
<< (the numbers below should match) << endl
<< endl
<< Bytes Allocated : << BytesAllocated << endl
<< Bytes Deleted : << BytesDeleted << endl
<< Calls to new : << CallsToNew << endl
<< Calls to delete : << CallsToDel << endl
<< endl ;
StatsFile.close() ; // explicitly close file to make
// doubly sure. }
} ;
Note that all the functions are inlined in order to not excessively impact the performance of the code being profiled.
- 2. For the class you want to monitor, overload the respective new and delete functions. After the statistics are updated, the easiest way to actually allocate the memory for most situations is to call the global operators to allocate and de-allocate memory. To call a global operator, use the member-of operator (::) without a scope name, that is, ::new. You must also make the statistics-keeping object accessible to the new and delete functions of your class. In this case, using a global variable was convenient, but it could just as easily have been a data member of the class. The following code shows the overloaded new and delete functions:
void* operator new( size_t size )
{
stats.newcalled( size ) ;
return (void*) ::new char[size] ; // allocate specified
// number of bytes with the actual new operator }
void operator delete( void* mem, size_t size )
{
stats.delcalled( size );
::delete[] mem ;
}
- 3. When allocating memory with code like this
::new char[size]
be sure to use the proper version of delete to free the memory:
::delete[] mem
It is easy to become confused when implementing the singular version of new and delete that actually allocates an array of data.
- 4. Its important to remember that byte sizes are not the same across platforms; a DOS char type is 1 byte, but some systems (such as Unicode) use 2-byte characters. When overloading new and delete in production code, it is important to write code to make this distinction to prevent your program from using two (or more) times the amount of memory it should.
How It Works
After the new and delete operators are overloaded, single calls to allocate an instance of the class will be processed through the overloaded functions. The key thing to remember is that allocating arrays of that class will not call the overloaded new and delete functions. These are allocated though the global new[] and delete[] operators, which are not guaranteed to be overloadable on all C++ compiler implementations.
Comments
It is absolutely critical to remember to deallocate memory properly in the delete operator, or risk memory leaks in your program. The operating system or runtime library will clean up after your program when it terminates, but there is nothing more frustrating than watching 10 or more hours of calculations go down the drain because the program ran out of memory.
12.6 Overload the new and delete functions for arrays?
Problem
Now that I know how to overload the singular new and delete operators, it would be nice to overload the array-based new[] and delete[] operators, too.
Technique
Overloading the array allocation and de-allocation routines for a class is compiler dependent. Some compiler versions (namely Borland C++ 5) do not pass the correct array size to the delete[] operator, for instance, making accurate statistics keeping difficult. The sample code is located in the newdel\ex2 subdirectory, with pre-compiled executables from the DJGPP and Borland compilers.
|