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

Testing the Memory Allocation Tracker

133

All production code should be tested with a memory-leak tool, or run through code like this to see whether memory is being allocated and freed properly, not freed correctly, or freed more than once.

Testing the Memory Allocation

Tracker

In order to see how the allocation tracking code works, it is easiest to create a simple test driver that illustrates the various pieces of the system. Let’s create a simple test program to use the new and delete handlers we have created. The following steps show you how:

LISTING 24-4: MEMORY ALLOCATOR TEST DRIVER

int main(int argc, char **argv)

{

DumpUnfreed();

 

 

char

*c = new char[200];

 

 

DumpUnfreed();

 

3

char

*c2 = new char[256];

 

DumpUnfreed();

 

delete c; delete c; DumpUnfreed();

int *x = new int[20]; delete [] x; DumpUnfreed();

Foo *f = new Foo(); delete f;

Foo *af = new Foo[5]; delete [] af;

1.

2.

3.

 

Foo *af1 = new Foo[3];

In the code editor of your choice, open the exist-

delete af1;

ing file to hold the code for your test program.

}

In this example, I named the test program CH 24.

 

Type the code from Listing 24-4 into your file.

4. Compile the source file, using your favorite

 

Better yet, copy the code from the source file on

compiler on your favorite operating system.

 

this book’s companion Web site.

If you run the resulting executable, the program

 

Save the source code and close your code editor.

should give you the output shown in Listing 24-5.

 

LISTING 24-5: OUTPUT FROM THE MEMORY TRACKING PROGRAM

$ ./a.exe

----------------------- Allocations ----------------------

-----------------------------------------------------------

Total Unfreed: 0 bytes

Array operator new called

-----------------------

Allocations ----------------------

(0) ADDRESS a050648

Size: 200 unfreed

-----------------------------------------------------------

Total Unfreed: 200 bytes

Array operator new called

 

-----------------------

 

 

Allocations

----------------------

(0)

ADDRESS

a050648

Size: 200

unfreed

(1)

ADDRESS

a050770

Size: 256

unfreed

-----------------------------------------------------------

(continued)

134 Technique 24: Defining Your Own new and delete Handlers

LISTING 24-5 (continued)

Total Unfreed: 456 bytes

Basic operator

delete called

Basic operator

delete called

Unable to find

allocation for delete [168101448]

-----------------------

 

Allocations ----------------------

(1)

ADDRESS a050770

Size: 256 unfreed

-----------------------------------------------------------

Total Unfreed:

256 bytes

 

Array operator

new called

 

Array operator

delete called

-----------------------

 

Allocations ----------------------

(1)

ADDRESS a050770

Size: 256 unfreed

-----------------------------------------------------------

Total Unfreed:

256 bytes

4

Basic operator

new called

 

Foo Constructor called

 

Foo Destructor

called

 

Basic operator

delete called

Array operator

new called

 

Foo Constructor called

 

Foo Constructor called

 

Foo Constructor called

 

Foo Constructor called

 

Foo Constructor called

 

Foo Destructor

called

 

Foo Destructor

called

 

Foo Destructor

called

 

Foo Destructor

called

 

Foo Destructor

called

 

Array operator

delete called

 

 

 

 

There are a lot of interesting things to take out of this technique. First, it gives you a better appreciation of what goes on behind the scenes in a typical C++ program. Second, you can see right away how the allocations and de-allocations are being handled and where the leaks are. In our example, we can see at the end of the program that we have a single memory leak of 256 bytes (at 4 in Listing 24-5). Note that we print out the current state of the program several times, so it is only the last display that indicates the leak at the end of the program. The

others are left in to illustrate how the program is allocating memory. It’s obvious, from looking at the program code, that this occurs for the c2 allocation (see 3 in Listing 24-4). We simply need to add a delete call for the character pointer and all will be copacetic.

The other interesting thing to note in this technique is which C++ new operator is called when. If you allocate a character pointer, for example, it calls the array version of the new operator. This situation is

Testing the Memory Allocation Tracker

135

counterintuitive — after all, you’re allocating a single character pointer — but it makes sense if you really think about it. It’s an array of characters that we happen to treat as a single string, which gives us the array version of the new operator. Likewise, when we allocate a single object, it calls the basic operator for the new allocation.

Before we leave this concept, there’s one more potential mess worth looking at. Try adding the following code to the end of your driver application:

Foo *af1 = new Foo[3]; delete af1;

If you compile this snippet of code at the end of your driver program, and then run the program, you will see the following output:

Array operator new called Foo Constructor called Foo Constructor called Foo Constructor called Foo Destructor called

Basic operator delete called

Unable to find allocation for delete [168101480]

Looking at the output, you will notice that the constructor for the class was called three times, for the three objects we created. The destructor, however, was only called once. Worse, because the block of memory allocated is actually three separate objects, our deletion routine couldn’t find it to delete it.

Moral: Always call the right version of delete for the corresponding version of new.

The new new operator

In C++, there is another version of the new operator called the new in place operator. This operator invokes the constructor for an object and sets it to point to a specific block of memory that is passed into it. If you are implementing a system that uses an embedded processor, where you cannot “really” allocate memory, or if you have an object pool, you might consider such a choice.

Add a memory tracker to every application you create. You can conditionally compile in the code to see how things are going at any stage of the application-development phase, and can use the resulting reports for quality assurance.

25 Implementing

Properties

Technique

Save Time By

Understanding properties in C++

Implementing a

Property class

Testing your Property class

If you are accustomed to programming in the “new” languages, such as Java or C#, you are probably already familiar with the concept of properties. Essentially, properties are public elements of a class that

have their own methods for getting and setting their values. Unlike traditional public values, however, a property cannot be accessed directly by the programmer — even though it looks like it can. A property has set and get functions that are invoked when you attempt to write or read to the property values. C++ has no direct implementation of properties in the language. This is really a shame, because properties save a lot of time for the end user by making it easier to read and write data values within the class.

For example, suppose you have a class named Foo that contains a property called age. This property can be set by the application developer, but only to values within the range of (say) 18 to 80. Now, in a “standard” C++ application, you could define the class with a public member such as in the following:

class Foo

{

public:

Foo()

{

}

int age;

};

If you had such a class, you could then write application code to directly set the age property, like this:

int main()

{

Foo f; f.age = 22;

}