Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ Timesaving Techniques (2005) [eng].pdf
Скачиваний:
73
Добавлен:
16.08.2013
Размер:
8.35 Mб
Скачать

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);

5
6

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.