
- •Introduction
- •Saving Time with This Book
- •Conventions Used in This Book
- •Part II: Working with the Pre-Processor
- •Part III: Types
- •Part IV: Classes
- •Part V: Arrays and Templates
- •Part VI: Input and Output
- •Part VII: Using the Built-in Functionality
- •Part VIII: Utilities
- •Part IX: Debugging C++ Applications
- •Part X: The Scary (or Fun!) Stuff
- •Icons Used in This Book
- •Creating and Implementing an Encapsulated Class
- •Creating a Mailing-List Application
- •Testing the Mailing-List Application
- •Customizing a Class with Polymorphism
- •Testing the Virtual Function Code
- •Why Do the Destructors Work?
- •Delayed Construction
- •The cDate Class
- •Testing the cDate Class
- •Creating the Header File
- •Testing the Header File
- •The Assert Problem
- •Fixing the Assert Problem
- •Using the const Construct
- •Identifying the Errors
- •Fixing the Errors
- •Fixing What Went Wrong with the Macro
- •Using Macros Appropriately
- •Using the sizeof Function
- •Evaluating the Results
- •Using sizeof with Pointers
- •Implementing the Range Class
- •Testing the Range Class
- •Creating the Matrix Class
- •Matrix Operations
- •Multiplying a Matrix by a Scalar Value
- •Multiplying a Matrix by Scalar Values, Take 2
- •Testing the Matrix Class
- •Implementing the Enumeration Class
- •Testing the Enumeration Class
- •Implementing Structures
- •Interpreting the Output
- •Defining Constants
- •Testing the Constant Application
- •Using the const Keyword
- •Illustrating Scope
- •Interpreting the Output
- •Using Casts
- •Addressing the Compiler Problems
- •Testing the Changes
- •Implementing Member-Function Pointers
- •Updating Your Code with Member-Function Pointers
- •Testing the Member Pointer Code
- •Customizing Functions We Wrote Ourselves
- •Testing the Default Code
- •Fixing the Problem
- •Testing the Complete Class
- •Implementing Virtual Inheritance
- •Correcting the Code
- •Rules for Creating Overloaded Operators
- •Using Conversion Operators
- •Using Overloaded Operators
- •Testing the MyString Class
- •Rules for Implementing new and delete Handlers
- •Overloading new and delete Handlers
- •Testing the Memory Allocation Tracker
- •Implementing Properties
- •Testing the Property Class
- •Implementing Data Validation with Classes
- •Testing Your SSN Validator Class
- •Creating the Date Class
- •Testing the Date Class
- •Some Final Thoughts on the Date Class
- •Creating a Factory Class
- •Testing the Factory
- •Enhancing the Manager Class
- •Implementing Mix-In Classes
- •Testing the Template Classes
- •Implementing Function Templates
- •Creating Method Templates
- •Using the Vector Class
- •Creating the String Array Class
- •Working with Vector Algorithms
- •Creating an Array of Heterogeneous Objects
- •Creating the Column Class
- •Creating the Row Class
- •Creating the Spreadsheet Class
- •Testing Your Spreadsheet
- •Working with Streams
- •Testing the File-Reading Code
- •Creating the Test File
- •Reading Delimited Files
- •Testing the Code
- •Creating the XML Writer
- •Testing the XML Writer
- •Creating the Configuration-File Class
- •Setting Up Your Test File
- •Building the Language Files
- •Creating an Input Text File
- •Reading the International File
- •Testing the String Reader
- •Creating a Translator Class
- •Testing the Translator Class
- •Creating a Virtual File Class
- •Testing the Virtual File Class
- •Using the auto_ptr Class
- •Creating a Memory Safe Buffer Class
- •Throwing and Logging Exceptions
- •Dealing with Unhandled Exceptions
- •Re-throwing Exceptions
- •Creating the Wildcard Matching Class
- •Testing the Wildcard Matching Class
- •Creating the URL Codec Class
- •Testing the URL Codec Class
- •Testing the Rot13 Algorithm
- •Testing the XOR Algorithm
- •Implementing the transform Function to Convert Strings
- •Testing the String Conversions
- •Implementing the Serialization Interface
- •Creating the Buffer Class
- •Testing the Buffer Class
- •Creating the Multiple-Search-Path Class
- •Testing the Multiple-Search-Path Class
- •Testing the Flow Trace System
- •The assert Macro
- •Logging
- •Testing the Logger Class
- •Design by Contract
- •Adding Logging to the Application
- •Making Functions Inline
- •Avoiding Temporary Objects
- •Passing Objects by Reference
- •Choosing Initialization Instead of Assignment
- •Learning How Code Operates
- •Testing the Properties Class
- •Creating the Locking Mechanism
- •Testing the Locking Mechanism
- •Testing the File-Guardian Class
- •Implementing the Complex Class
- •Creating the Conversion Code
- •Testing the Conversion Code
- •A Sample Program
- •Componentizing
- •Restructuring
- •Specialization
- •Index

51 |
Using the auto_ptr |
|
|
|
Class to Avoid |
Technique |
Memory Leaks |
|
Save Time By
Preventing memory leaks caused by overwritten pointers
Introducing the auto_ptr class
Implementing the auto_ptr class
Interpreting the output
There are numerous products on the market for detecting and resolving memory leaks. And in this book, I have included several techniques for discovering memory leaks as well. However, the
best way to handle memory leaks is to not have them in the first place. Defensive programming techniques can avoid memory-leak problems and save you immense amounts of time and trouble in the long-run.
Rather than try to find and fix problems as they occur, you’d be better off utilizing techniques that avoid the problem in the first place. This way you have more time to spend on solving problems directly related to user interaction and needs and less time to worry about trivial problems that must be fixed before you can get to those issues.
One of the most insidious memory leak issues is that of pointers that are overwritten or failed to de-allocate. If a pointer is not de-allocated, a
memory leak will occur. If enough memory leaks occur, your program will not be able to allocate new memory and will likely crash. With overwritten pointers, the memory that they point at is not the same memory that was allocated. As a result, the original memory is not de-allocated, which causes the memory leak problem. Alternatively, the overwritten pointer may point at something important in memory, and when it is dereferenced and used to modify that memory, it will cause a program crash. The STL provides a wonderful tool for avoiding this particular problem: the auto_ptr class. This class ensures that a pointer is always deleted when its work is done, even if it has been copied over, ignored, or transferred to an orphan object. This technique explores how to use the auto_ptr class to avoid problems in your own code.
Using the auto_ptr Class
The auto_ptr class makes code cleaner by removing the need to check for allocations and de-allocations of objects all over your code. Let’s look at the steps necessary to use the auto_ptr class in your own code. Essentially, there is only one real “step” involved, which is to wrap an allocated pointer in an auto_ptr template object. We will see how the object is allocated and then freed when the auto_ptr template object goes out of scope.

304 Technique 51: Using the auto_ptr Class to Avoid Memory Leaks
1. In the code editor of your choice, create a new file to hold the code for the technique.
In this example, the file is named ch51.cpp, although you can use whatever you choose. This file will contain the source code for our classes.
2. Type the code from Listing 51-1 into your file.
Better yet, copy the code from the source file on this book’s companion Web site.
LISTING 51-1: USING THE AUTO_PTR CLASS WITH YOUR OWN
FUNCTIONS
#include <iostream> #include <memory>
using namespace std;
class Tracker
{
private:
static int _allocations; static int _frees;
public:
Tracker( void )
{
_allocations ++;
}
Tracker( const Tracker& aCopy )
{
_allocations ++;
}
~Tracker()
{
_frees ++;
}
static int Allocations()
{
return _allocations;
}
static int Frees()
{
return _frees;
}
static void reset()
{
_allocations = 0; _frees = 0;
}
static void report()
{
cout << |
“Tracker Class:” << |
endl; |
cout << |
“Allocations: “ << _alloca- |
|
tions |
<< endl; |
|
cout << |
“Frees: “ << _frees |
<< endl; |
}
};
int Tracker::_allocations = 0; int Tracker::_frees = 0;
void func1()
{
Tracker t1;
Tracker *t2 = new Tracker(); Tracker t3 = *t2;
Tracker t4 = t1;
} |
t2 = new Tracker(); |
1 |
|
void func2() |
|
2 |
|
{ |
|
|
Tracker t1;
auto_ptr<Tracker> t2( new Tracker ); Tracker t3 = *t2;
Tracker t4 = t1;
t2.reset( new Tracker );
} |
|
|
void call_an_exception_function() |
|
|
{ |
|
|
throw 1; |
|
|
} |
|
|
void func3() |
|
3 |
{ |
|
|
Tracker *t = new Tracker; |
|
|
call_an_exception_function(); |
|
|
delete t; |
|
|
} |
|
|
void func4() |
|
|
{ |
4 |
|
auto_ptr<Tracker> t(new Tracker); |

Using the auto_ptr Class 305
call_an_exception_function();
}
int main(void)
{
cout << “Running function 1:” << endl; func1();
Tracker::report();
Tracker::reset();
cout << endl;
cout << “Running function 2:” << endl; func2();
Tracker::report();
Tracker::reset();
cout << endl;
cout << “Running function 3:” << endl; try
{
func3();
}
catch ( ... )
{
}
Tracker::report();
Tracker::reset();
cout << endl;
cout << “Running function 4:” << endl; try
{
func4();
}
catch ( ... )
{
}
Tracker::report();
}
Our test code illustrates two separate ways in which memory can be leaked:
You can forget to de-allocate a pointer, as shown in the func1 function at 1.
In this case, we are simply allocating a new
object and never freeing the object, which creates a memory leak. The function called func2, labeled 2, shows the same code using an auto_ptr class rather than a plain allocation.
You can allocate and free an object, but because the function calls something that throws an exception, the de-allocation line will never be run and a memory leak will occur. This more subtle memory leak is shown in
function func3 at 3. Function func4 shows the same basic code using an auto_ptr template instead, as shown in the line marked 4.
3.Save the source code in your editor and close the editor application.
4.Compile the application using your favorite compiler on your favorite operating system.
5.Run the application in the console window.
If you have done everything right, the application should give you the output shown in Listing 51-2.
LISTING 51-2: OUTPUT FROM THE AUTO_PTR TEST PROGRAM
$ ./a.exe
Running function 1: Tracker Class: Allocations: 5 Frees: 3
Running function 2:
Tracker Class:
Allocations: 5
Frees: 5
Running function 3:
Tracker Class:
Allocations: 1
Frees: 0
Running function 4:
Tracker Class:
Allocations: 1
Frees: 1

306 Technique 51: Using the auto_ptr Class to Avoid Memory Leaks
As you can see from the output, the class Tracker tracks how many times the various constructors are called, and how many times the destructor is called
in each run. The report is done via the Tracker |
|
|
class report method, as shown in Listing 51-1 at |
|
5. |
the |
|
|
Note that we reset the count each time, using |
|
|
reset function as shown at 6. In an ideal situation, with no memory leaks, the numbers for allocations and frees should be the same. For functions func1 and func3, the allocation and free numbers are not the same, indicating a memory leak. For functions func2 and func4, the auto_ptr cases, the allocations and frees match up, indicating that there was no memory leak.
The functions we invoke here (func1, func2, func3, and func4) show the various ways in which memory can be leaked in an application. As you can see, the “normal” way of doing things results in numerous insidious memory leaks that are hard to track down. Compare the auto_ptr cases, which, even with exceptional events, always free their memory.
Rules for using the auto_ptr class
There are no free lunches in the programming world, and the auto_ptr class is no exception to that rule. There are certain times you should not use an auto_ptr, and certain rules you must understand — such as the following:
You cannot use auto_ptrs in standard template collections such as the STL. Because the STL does not follow the standard rules for copying objects, auto_ptrs will not be destroyed properly. The designers of the STL made this choice and actually created templates that would not compile with auto_ptrs.
If you copy an auto_ptr, you must not use the original again, as the pointer is transferred from one object to the other.
The copy constructor for an auto_ptr is completely different than the copy constructor for a normal object or pointer. Do not treat them equivalently. Auto_ptr copy constructors transfer control of the pointer they contain, they do not make copies of it.
Other than that, the class is really a godsend for programmers. When you are working with pointers, use the auto_ptr class early and often in your programming applications.