The new and delete operators provide a more flexible approach than automatic and static variables. They manage a pool of memory, which C++ refers to as the free store or heap. This pool is separate from the memory used for static and automatic variables. As Listing 4.22 shows, new and delete enable you to allocate memory in one function and free it in another. Thus, the lifetime of the data is not tied arbitrarily to the life of the program or the life of a function. Using new and delete together gives you much more control over how a program uses memory than does using ordinary variables. However, memory management becomes more complex. In a stack, the automatic addition and removal mechanism results in the part of memory in use always being contiguous. But the interplay between new and delete can leave holes in the free store, making keeping track of where to allocate new memory requests more difficult.

Stacks, Heaps, and Memory Leaks

What happens if you don’t call delete after creating a variable on the free store (or heap) with the new operator? The variable or construct dynamically allocated on the free store continues to persist if delete is not called, even though the memory that contains the pointer has been freed due to rules of scope and object lifetime. In essence, you have no way to access the construct on the free store because the pointer to the memory that contains it is gone. You have now created a memory leak. Memory that has been leaked remains unusable through the life of the program; it’s been allocated but can’t be deallocated. In extreme (though not uncommon) cases, memory leaks can be so severe that they use up all the memory available to the application, causing it to crash with an out-of-memory error. In addition, these leaks may negatively affect some operating systems or other applications running in the same memory space, causing them, in turn, to fail.

Even the best programmers and software companies create memory leaks. To avoid them, it’s best to get into the habit of joining your new and delete operators immediately, planning for and entering the deletion of your construct as soon as you dynamically allocate it on the free store. C++’s smart pointers (Chapter 16) help automate the task.

Note

Pointers are among the most powerful of C++ tools. They are also the most dangerous because they permit computer-unfriendly actions, such as using an uninitialized pointer to access memory or attempting to free the same memory block twice. Furthermore, until you get used to pointer notation and pointer concepts through practice, pointers can be confusing. Because pointers are an important part of C++ programming, they weave in and out of future discussions in this book. This book discusses pointers several more times. The hope is that each exposure will make you more comfortable with them.

Combinations of Types

This chapter has introduced arrays, structures, and pointers. These can be combined in various ways, so let’s review some of the possibilities, starting with a structure:

struct antarctica_years_end

{

    int year;

 /* some really interesting data, etc. */

};

We can create variables of this type:

antarctica_years_end s01, s02, s03;  // s01, s02, s03 are structures

We can then access members using the membership operator:

s01.year = 1998;

We can create a pointer to such a structure:

antarctica_years_end * pa = &s02

Provided the pointer has been set to a valid address, we then can use the indirect membership operator to access members:

pa->year = 1999;

We can create arrays of structures:

antarctica_years_end trio[3]; // array of 3 structures

We then can use the membership operator to access members of an element:

trio[0].year = 2003;  // trio[0] is a structure

Here, trio is an array, but trio[0] is a structure, and trio[0].year is a member of that structure. Because an array name is a pointer, we also can use the indirect membership operator:

(trio+1)->year = 2004; // same as trio[1].year = 2004;

We can create an array of pointers:

const antarctica_years_end * arp[3] = {&s01, &s02, &s03};

This is starting to look a bit complicated. How can we access data with this array? Well, if arp is an array of pointers, then arp[1] must be a pointer, and we can use the indirect membership operator with it to access a member:

std::cout << arp[1]->year << std::endl;

We can create a pointer to such an array:

const antarctica_years_end ** ppa = arp;

Перейти на страницу:

Все книги серии Developer's Library

Похожие книги