Thursday, September 3, 2009

How to write Windows programs in C++. LEARN JUST IN 14 DAYS............ by Kent Reisdorph ..........trust me its really good guys

Day 3

Week1

68 Day 3

Pointers: Welcome to My Nightmare

Pointers are one of the most confusing aspects of the C++ language. They are also one of the

most powerful features of C++. My goal in this section is not to teach you the textbook

definition of pointers, but rather to teach you pointers in the context of how you will use them

in your C++Builder programs. So what is a pointer? It’s a variable that holds the address of

another variable. There, that wasn’t so bad, was it? I wish it were that simple! Because a pointer

holds the address of another variable, it is said to “point to” the second variable. This is called

indirection because the pointer does not have a direct association with the actual data, but

rather an indirect association.

A pointer is a variable that holds the address of another variable.

Because the pointer does not have a direct association with the actual data,

indirection is the term used when referring to this indirect association.

Let’s look at an example. Earlier we talked about arrays. Let’s say that you had an array of ints.

You could access the individual elements of the array using the subscript operator, as I talked

about on Day 1, “Getting Your Feet Wet”:

int array[] = { 5, 10, 15, 20, 25 };

int someVariable = array[3]; // the value 20

You could also use a pointer to accomplish the same thing:

int array[] = { 5, 10, 15, 20, 25 };

int* ptr = array;

int someVariable = ptr[3];

In this example, the memory location of the beginning of the array is assigned to the pointer

named ptr. Note that the pointer is a pointer of the data type int and that the indirection

operator (the * symbol) is used when you declare a pointer. You can declare a pointer to any

of the integral data types (int, char, long, short, and so on), as well as a pointer to objects

(structures or classes). After the assignment the pointer contains the memory address of the

start of the array, and as such points to the array.

The name of an array variable, when used without the subscript

operator, returns the memory address of the first element of the array.

Put another way, the variable name of an array is a pointer, to the start

of the array. That makes it possible to assign an array to a pointer, as in

the preceding example.

NEW TERM

NOTE

NEW TERM

Up to Your Neck in C++ 69

3

In this case you can now use the pointer, ptr, just as you would the array name itself. I can

hear you wondering, though: “But why would you want to?” The truth is that in this example

there is no real benefit to using a pointer. The real benefit of pointers is when it comes to

creating objects dynamically in memory. In that case, a pointer is necessary to access the

object. I really can’t go on with this discussion, though, until I digress a moment and talk

about the two ways you can create variables and objects.

Local Versus Dynamic Memory Usage

So far all my sample programs have used local allocation of objects—that is, the memory

required for a variable or object is obtained from the program’s stack.

Local allocation means that the memory required for a variable or object is obtained

from the program’s stack.

The stack is an area of working memory set aside by the program when the program

starts.

Any memory the program needs for things such as local variables, function calls, and so on

is taken from the stack. This memory is allocated as needed and then freed when it is no longer

needed. Usually this happens when the program enters a function or other local code block.

Memory for any local variables the function uses is allocated when the function is entered.

When the function returns, all of the memory allocated for the function’s use is freed. It all

happens for you automatically; you don’t have to give any thought to how or if the memory

is freed.

Local allocation has its good points and its bad points. On the plus side, memory can be

allocated from the stack very quickly. The downside is that the stack is of a fixed size and

cannot be changed as the program runs. If your program runs out of stack space, weird things

start to happen. Your program might just crash, it might start behaving oddly, or it might

seem to perform normally but crash when the program terminates. This is less of a problem

in the 32-bit world than it is in 16-bit programming, but it’s still a consideration.

For things like variables of the built-in data types and small arrays, there is no point in doing

anything other than local allocation. But if you are going to be using large arrays, structures,

or classes, you will probably want to use dynamic allocation from the heap. This amounts to

your free physical RAM plus all of your free hard disk space. In other words, you could easily

have 100MB of heap memory available on a typical Windows system. The good news here

is that you have virtually unlimited memory available for your programs. The bad news is that

memory allocated dynamically requires some additional overhead, and as such is just a

smidgen slower than memory allocated from the stack. In most programs the extra overhead

is not noticed in the least. An additional drawback of dynamic allocation is that it requires

more from the programmer. Not a lot more, mind you, but a little.

NEW TERM

NEW TERM

70 Day 3

Dynamic allocation means that memory required for an object is allocated from the

heap.

The heap in a Windows program refers to all of your computer’s virtual memory.

Dynamic Allocation and Pointers

In a C++ program, memory is allocated dynamically by using the new operator.

I’m going to talk about new a little later in the chapter, but you need a little sampler as I

continue the discussion about pointers. Earlier I talked about structures and used the

mailingListRecord structure as an example. Allocating a structure from the stack looks like

this:

mailingListRecord listArray;

strcpy(listArray.firstName, “Ian”);

strcpy(listArray.lastName, “Spencer”);

// etc.

That’s what I did earlier when I talked about structures. Now I’ll create the array dynamically

rather than locally:

mailingListRecord* listArray;

listArray = new mailingListRecord;

strcpy(listArray->firstName, “Ian”);

strcpy(listArray->lastName, “Spencer”);

// etc.

The first line declares a pointer to a mailingListRecord structure. The next line initializes the

pointer by creating a new instance of a mailingListRecord structure dynamically. This is the

process by which you dynamically create and access objects in C++.

And Now Back to Our Program

Now you begin to see where pointers fit into the scheme of things. When you create an object

dynamically, the new operator returns a pointer to the object in memory. You need that

pointer to be able to do anything with the object. Figure 3.1 illustrates how the pointer points

to the object in memory. Note that although the memory for the dynamically created object

is allocated from heap memory, the actual pointer is a local variable and is allocated from the

stack.

NEW TERM

NEW TERM

NEW TERM

Up to Your Neck in C++ 71

Let’s go back to a code snippet you saw earlier: 3

mailingListRecord* listArray;

listArray = new mailingListRecord;

strcpy(listArray->firstName, “Ian”);

strcpy(listArray->lastName, “Spencer”);

// etc.

On the third line you see that the firstName data member of the structure is accessed using

the indirect member operator (->) rather than the structure member operator. (We discussed

the structure member operator yesterday in the section titled “Structures.” The term direct

member operator is also used and is more representative than structure member operator, so

I will use direct member operator from now on.) When you create an object dynamically, you

must access the object’s data members and functions using this operator.

Creating an array of structures dynamically requires a bit more work. Again, here’s the stackbased

version:

mailingListRecord listArray[3];

listArray[0].zip = 57441;

And the dynamic version:

mailingListRecord* listArray[3];

for (int i=0;i<3;i++)

listArray[i] = new mailingListrecord;

listArray[0]->zip = 57441;

Note that I have to create a new instance of the structure for each element of the array. Notice

also that to access a data member of the array, I use the indirect membership operator

combined with the subscript operator.

Figure 3.1.

A pointer to an object

in memory. mailingListRecord*

listArray

stack memory

listArray

listArray points to

address 0x00780E50,

which is an instance

of the mailingListRecord

structure in memory

mailingListRecord

structure in memory

heap memory

0x00780E50

firstName

lastName

address

city

state

zip

72 Day 3

Uninitialized pointers contain random values just like any other

uninitialized variable. Attempting to use an uninitialized pointer can

wreak havoc on a program. In many cases, a pointer is declared and

immediately initialized:

MyArray* array = new MyArray;

Sometimes, however, you will declare a pointer and then not initialize

it until sometime later in the program. If you attempt to use the

pointer before initializing it, the pointer will point to some random

memory location, and modifying that memory could cause all sorts of

nasty problems. Often the problems caused by modifying unknown

memory do not show up immediately, making the bug appear to be

random. To be safe, you should initialize a pointer to 0 when you

declare it:

MyArray* array = 0;

If you attempt to use a NULL pointer (any pointer set to NULL or 0), you

will immediately get an access violation or GPF from Windows.

Although this may not sound like a good thing, it is certainly the lesser

of two evils. It is far better to have an immediate error at the point of

the infraction than to have a random problem that may show up

further down the road.

Dereferencing a Pointer

Frequently you will need to dereference a pointer in order to retrieve the contents of the

memory location (the object) that a pointer points to. Take the following example:

int x = 20;

int* ptrx = &x;

// later...

int z = *ptrx;

I can just imagine your frustration right now. What a mess! Take heart; it’s not quite as bad

as it might appear. The first line in this example declares an int variable called x and assigns

it a value of 20. The next line declares a pointer to an int and assigns to the pointer the address

of the variable x. This is done by using the address-of operator (&). In this example, the addressof

operator tells the compiler, “Give me the address of the variable x, not the value of x itself.”

After the assignment, ptrx contains the memory address of x. Later on in the program you

might need to get the value of the object pointed to by ptrx. You might think to try this:

int z = ptrx; // wrong!

WARNING

Up to Your Neck in C++ 73

3

That won’t work, however, because you are trying to assign a memory address to a regular

variable. When you try to compile this line, the compiler will spit back an error stating, Cannot

convert int* to int. That makes sense because you are dealing with two different types of

variables. So you need to dereference the pointer using the indirection operator:

int z = *ptrx;

This could be considered the opposite of the address-of operator. Here you don’t want the

actual value of ptrx because the actual value is a memory address. Instead you want the value

of the object pointed to by that memory address. So, in this case, the indirection operator tells

the compiler, “Give me the value of the object ptrx points to, not the actual value of ptrx.”

Dereferencing a pointer means to retrieve the contents of the memory location (the

object) that a pointer points to.

As you can see, the indirection operator is used to declare a pointer

(int* x;) and also to dereference a pointer (int z = *x;). The compiler

can tell from the context in which the indirection operator is used

what to do in each case. You don’t have to worry that the compiler

won’t know what you intend.

C++ syntax is largely a personal thing. I prefer to use the indirection

operator next to the data type when declaring a pointer, and next to the

pointer when dereferencing a pointer:

int* x;

SomeClass* aClass = new SomeClass;

char* s = new char[256];

int z = *x;

SomeClass temp = *aClass;

Others prefer to place the indirection operator next to the variable

name:

int *x;

// or even...

int * x;

I happen to think that the syntax I use makes the most sense, but

others could probably argue that their way is best, too. In the end,

settle on the method you like best and then stick to it.

NEW TERM

NOTE

NOTE

74 Day 3

Putting It Together

Let’s try to tie together what you have learned in the previous section. I’ll take the MAILLIST

program from Day 2, “Wading In Deeper,” and modify it so that it uses dynamic memory

allocation. This will require a few changes. First, take a look at the modified program, and

then I’ll explain the changes. Listing 3.1 contains the modified MAILLIST program.

Listing 3.1. POINTER.CPP.

1: #include

2: #include

3: #include

4: #pragma hdrstop

5: #include “structur.h”

6: void displayRecord(int, mailingListRecord mlRec);

7: int main(int, char**)

8: {

9: //

10: // create an array of pointers to

11: // the mailingListRecord structure

12: //

13: mailingListRecord* listArray[3];

14: //

15: // create an object for each element of the array

16: //

17: for (int i=0;i<3;i++)

18: listArray[i] = new mailingListRecord;

19: cout <<>

20: int index = 0;

21: //

22: // get three records

23: //

24: do {

25: cout << “First Name: “;

26: cin.getline(listArray[index]->firstName,

27: sizeof(listArray[index]->firstName) - 1);

28: cout << “Last Name: “;

29: cin.getline(listArray[index]->lastName,

30: sizeof(listArray[index]->lastName) - 1);

31: cout << “Address: “;

32: cin.getline(listArray[index]->address,

33: sizeof(listArray[index]->address) - 1);

34: cout << “City: “;

35: cin.getline(listArray[index]->city,

36: sizeof(listArray[index]->city) - 1);

37: cout << “State: “;

38: cin.getline(listArray[index]->state,

39: sizeof(listArray[index]->state) - 1);

40: char buff[10];

41: cout << “Zip: “;

Up to Your Neck in C++ 75

3

42: cin.getline(buff, sizeof(buff) - 1);

43: listArray[index]->zip = atoi(buff);

44: index++;

45: cout <<>

46: }

47: while (index <>

48: //

49: // display the three records

50: //

51: clrscr();

52: //

53: // must dereference the pointer to pass an object

54: // to the displayRecord function.

55: //

56: for (int i=0;i<3;i++)>

57: displayRecord(i, *listArray[i]);

58: }

59: //

60: // ask the user to choose a record

61: //

62: cout << “Choose a record: “;

63: char rec;

64: do {

65: rec = getch();

66: rec -= 49;

67: } while (rec <> 2);

68: //

69: // assign the selected record to a temporary variable

70: // must dereference here, too

71: //

72: mailingListRecord temp = *listArray[rec];

73: clrscr();

74: cout <<>

75: //

76: // display the selected recrord

77: //

78: displayRecord(rec, temp);

79: getch();

80: return 0;

81: }

82: void displayRecord(int num, mailingListRecord mlRec)

83: {

84: cout << “Record “ <<>

85: cout << “Name: “ <<>

86: cout <<>

87: cout <<>

88: cout << “Address: “ <<>

89: cout <<>

90: cout <<>

91: cout <<>

92: cout <<>

93: cout <<>

94: }

76 Day 3

First, on line 13 I declared the listArray array as an array of pointers. Following that,

I created objects for each element of the array. This takes place in the for loop on

lines 17 and 18. After that, I changed the direct membership operators (.) to indirect

membership operators (->). I also have to dereference the pointers on line 57 and again on

line 72. This is necessary because an object is expected and we cannot use a pointer in place

of an object. Notice that the displayRecord function (starting on line 82) doesn’t change. I

haven’t changed the fact that the mailingListRecord structure is passed to the function by

value, so the code in the function doesn’t need to be modified.

If you’ve had previous experience with C++, you may have noticed that this program has a

bug in it. I’ll let you in on the secret before the end of the chapter.

References

A reference is a special type of pointer that allows you to treat a pointer like a regular

object.

References, like pointers, can be confusing. A reference is declared using the reference operator.

The symbol for the reference operator is the ampersand (&) which is the same symbol used

for the address-of operator (don’t worry, the compiler knows how to keep it all straight). As

I said, a reference allows you to treat a pointer like an object. Here’s an example:

MyStruct* pStruct = new MyStruct;

MyStruct& ref = *pStruct;

ref.X = 100;

Notice that with references you use the direct member operator rather than the indirect

member operator as you do with pointers. Now you can get rid of all of those pesky ->

operators! Although you won’t use references a lot, they can be very handy when you need

them. By the way, this code snippet could be condensed a little. Here’s how I would write

it in a real program:

MyStruct& ref = *new MyStruct;

ref.X = 100;

Although this might look odd, it does exactly the same thing as the first example. Combining

statements like this is common and avoids unnecessary overhead.

Let’s go once more to the MAILLIST example. This time I’ll modify it by implementing a

reference in the do-while loop. Actually, I’ll be modifying the POINTER example found in

Listing 3.1. The new program, found in Listing 3.2, illustrates this change.

ANALYSIS

NEW TERM

Up to Your Neck in C++ 77

3

Listing 3.2. REFERENC.CPP.

1: #include

2: #include

3: #include

4: #pragma hdrstop

5: #include “structur.h”

6: void displayRecord(int, mailingListRecord mlRec);

7: int main(int, char**)

8: {

9: cout <<>

10: //

11: // create an array of mailingListRecord structures

12: //

13: mailingListRecord* listArray[3];

14: //

15: // create objects for each record

16: //

17: for (int i=0;i<3;i++)

18: listArray[i] = new mailingListRecord;

19: int index = 0;

20: //

21: // get three records

22: //

23: do {

24: // create a reference to the current record

25: mailingListRecord& rec = *listArray[index];

26: cout << “First Name: “;

27: cin.getline(rec.firstName, sizeof(rec.firstName) - 1);

28: cout << “Last Name: “;

29: cin.getline(rec.lastName, sizeof(rec.lastName) - 1);

30: cout << “Address: “;

31: cin.getline(rec.address, sizeof(rec.address) - 1);

32: cout << “City: “;

33: cin.getline(rec.city, sizeof(rec.city) - 1);

34: cout << “State: “;

35: cin.getline(rec.state, sizeof(rec.state) - 1);

36: char buff[10];

37: cout << “Zip: “;

38: cin.getline(buff, sizeof(buff) - 1);

39: rec.zip = atoi(buff);

40: index++;

41: cout <<>

42: }

43: while (index <>

44: //

45: // display the three records

46: //

47: clrscr();

48: //

49: // must dereference the pointer to pass an object

50: // to the displayRecord function.

51: //

continues

78 Day 3

52: for (int i=0;i<3;i++)>

53: displayRecord(i, *listArray[i]);

54: }

55: //

56: // ask the user to choose a record

57: //

58: cout << “Choose a record: “;

59: char rec;

60: do {

61: rec = getch();

62: rec -= 49;

63: } while (rec <> 2);

64: //

65: // assign the selected record to a temporary variable

66: // must dereference here, too

67: //

68: mailingListRecord temp = *listArray[rec];

69: clrscr();

70: cout <<>

71: //

72: // display the selected recrord

73: //

74: displayRecord(rec, temp);

75: getch();

76: return 0;

77: }

78: void displayRecord(int num, mailingListRecord mlRec)

79: {

80: cout << “Record “ <<>

81: cout << “Name: “ <<>

82: cout <<>

83: cout <<>

84: cout << “Address: “ <<>

85: cout <<>

86: cout <<>

87: cout <<>

88: cout <<>

89: cout <<>

90: }

The only real change is in the do-while loop. Notice that a reference to a

mailingListRecord structure is declared. Each time through the loop, the reference

is assigned a different object (the next element in the array). Notice that I got rid of the

indirect membership operators and replaced them with the direct membership operators. As

I said earlier, a reference allows you to treat a pointer as an object. What that does for us in

this case is clean up the code a little and make it easier to read. Oh, for those of you keeping

score, this program has the same bug in it as does the POINTER example. I’ll remedy that at the

end of the chapter.

Listing 3.2. continued

ANALYSIS

Up to Your Neck in C++ 79

3

Although it might appear that references are preferred over pointers, that is not the case.

References have some peculiarities that make them unsuitable in many cases. For one thing,

references cannot be declared and then later assigned a value. They must be initialized when

declared. For instance, the following code snippet will result in a compiler error:

MyStruct* pStruct = new MyStruct;

MyStruct& ref;

ref = *pStruct;

ref.X = 100;

Another problem with references is that they cannot be set to 0 or NULL as pointers can. That

means you’ll have to take special care to ensure that a reference is not deleted twice. References

and pointers can often serve the same purpose, but neither is perfect in every programming

situation.

Passing Function Parameters by

Reference and by Pointer

Earlier I talked about passing objects to functions by value. I said that in the case of structures

and classes, it is usually better to pass those objects by reference rather than by value. Any

object can be passed by reference. This includes the primitive data types such as int and long,

as well as instances of a structure or class. To review, when you pass function parameters by

value, a copy of the object is made, and the function works with the copy. When you pass

by reference, a pointer to the object is passed and not the object itself. This has two primary

implications. First, it means that objects passed by reference can by modified by the function.

Second, passing by reference eliminates the overhead of creating a copy of the object.

The fact that an object can be modified by the function is the most important aspect of

passing by reference. Take this code, for instance:

void IncrementPosition(int& xPos, int& yPos)

{

xPos++;

yPos++;

}

int x = 20;

int y = 40;

IncrementPosition(x, y);

// x now equals 21 and y equals 41

Notice that when the function returns, both of the parameters passed have been incremented

by one. This is because the function is modifying the actual object via the pointer (remember

that a reference is a type of pointer).

80 Day 3

Remember that a function can return only one value. By passing

parameters by reference you can achieve the effect of a function

returning more than one value. The function still only returns one

value, but the objects passed by reference are updated, so the function

effectively returns multiple values.

As I said, the other reason to pass parameters by reference is to eliminate the overhead of

making a copy of the object each time the function is called. When dealing with primitive

data types, there is no real overhead involved in making a copy. When dealing with structures

and classes, however, the overhead is something to be considered. You should pass structures

of any consequence by reference, as the following code demonstrates:

// structure passed by reference

void someFunction(MyStructure& s)

{

// do some stuff with ‘s’

return;

}

MyStructure myStruct;

// do some stuff, then later...

someFunction(myStruct);

Notice that the function call looks exactly the same whether the object is being passed by

reference or by value.

Do you see a potential problem with passing by reference? If you pass by reference, you avoid

the overhead of making a copy of the object, but now the object can be modified by the

function. Sometimes you don’t want the object to be modified by the function. So what if you

want to pass by reference but make sure the object is not modified? Read on and I’ll tell you.

The const Keyword

The const keyword will allow you to declare a variable as constant.

Once a variable is declared with const, it cannot be changed. The solution, then, is to pass

by reference and make the object const:

void someFunction(const MyStruct& s)

{

// do some stuff with ‘s’

return;

}

MyStructure myStruct;

// later

someFunction(myStruct);

TIP

NEW TERM

Up to Your Neck in C++ 81

3

Now you are free to pass by reference and not worry that your object might be modified by

the function. Note that the function call itself stays the same and that only the function

definition (and declaration) is modified with the const keyword.

If you attempt to modify a const object, you will get a compiler error

stating, Cannot modify a const object. The following code will

generate that error message:

void someFunction(const MyStruct& s)

{

s.dataMember = 100; // cannot modify a const object

return;

}

Once you declare an object as const, the compiler will make sure you

don’t modify the object.

Note that the object is const only within the function. The object can be modified both

before and after the function returns (provided it was not initially declared as const).

Passing by pointer is essentially the same as passing by reference. Passing by pointer has a

couple of syntactical headaches that make it less desirable than passing by reference. Let’s take

the IncrementPosition() function from the first example in this section and modify it to pass

by pointer rather than by reference:

void IncrementPosition(int* xPos, int* yPos)

{

*xPos++; // dereference, then increment

*yPos++;

}

Note that the pointer has to be dereferenced before it can be incremented. Most of the time

your needs will be best served by passing by reference, but you may pass by pointer if a

situation dictates the need. When passing char arrays, you will usually pass by pointer rather

than by reference because you can use a pointer to a char array and the name of the array

interchangeably. When passing character arrays, it is better to pass by pointer.

The new and delete Operators

Up to this point I have been talking primarily about aspects of the C++ language that come

from C. From this point on we’ll be looking at features that are specific to the C++ language.

The new and delete operators are two important C++ language features.

NOTE

82 Day 3

As mentioned in the preceding section, memory in a C++ program is allocated dynamically

using operator new. You free memory using the delete operator. Unless you have previously

programmed in C, you might not appreciate the simplicity of new and delete. In C programs,

you use malloc(), calloc(), realloc(), and free() to dynamically allocate memory.

Windows really complicates things by offering a whole raft of local and global memoryallocation

functions. Although this is not exactly difficult, it can be confusing to say the least.

C++ removes those headaches through the use of new and delete.

A new World Order

You’ve already seen new in action, so let’s review. As discussed earlier, you can allocate

memory locally (from the stack) or dynamically (from the heap). The following code snippet

shows examples of allocating two character arrays. One is allocated from the stack (local

allocation), and the other is allocated from the heap (dynamic allocation):

char buff[80];

char* bigBuff = new char[4096];

In the first case the buffer size is insignificant, so it doesn’t really matter whether the stack

or the heap is used. In the second case a large char array is needed, so it makes sense to allocate

it from the heap rather than from the stack. This preserves stack space. In the case of arrays

(remember, a string is just an array of type char), the dynamic and local flavors can be used

interchangeably. That is, they use the same syntax:

strcpy(buff, “Ricky Rat”);

strcpy(bigBuff, “A very long string that goes on and on...”);

// later on...

strcpy(bigBuff, buff);

Remember that the name of an array when used by itself points to the first memory location

of the array. A pointer also points to the first memory location of the array, so that is why the

two forms can be used interchangeably.

If the new operator fails to allocate the requested memory, it returns

NULL. In theory, you should check the pointer after calling new to ensure

that it contains a non-zero value:

char* buff = new char[1024];

if (buff) strcpy(buff, “Buteo Regalis”);

else ReportError(); // something went wrong

In reality, if the new operator fails in a 32-bit Windows program, the

entire system is in trouble, and neither your program nor any other will

be running for long.

NOTE

Up to Your Neck in C++ 83

3

If you are attempting to allocate very large chunks of memory (several

megabytes in size) or are trying to allocate memory at critical points in

your program, you should check the pointer for validity before continuing.

For routine memory-allocation chores, you can probably get

by without checking to ensure that the new operator succeeded.

delete

All memory allocated must be deallocated (released or freed) after you are done with the

memory. With local objects, this happens for you automatically, and you don’t have to worry

about it. The memory manager allocates the memory your object needs from the stack and

then frees that memory when the object goes out of scope (usually when a function returns

or when the code block in which the object was declared ends). When using dynamic memory

allocation, the programmer must take the responsibility of freeing any memory allocated with

the new operator.

Freeing memory allocated with new is accomplished with the delete operator.

All calls to new need to have a matching delete. If you do not free all

memory allocated with the new operator, your program will leak

memory. You need to be diligent in matching new/delete pairs.

Using the delete operator is frightfully easy:

SomeObject* myObject = new SomeObject;

// do a bunch of stuff with myObject

delete myObject; // so long!

That’s all there is to it! There isn’t a lot to the delete operator, but there are a couple of things

about pointers and delete that you should be aware of. The first is that you must not delete

a pointer that has already been deleted, or you will get access violations and all sorts of other

fun stuff. Second, it is okay to delete a pointer that has been set to 0. So what does that mean

in the real world? Let me explain.

Sometimes you declare a pointer just in case it might be used, but you don’t know for sure

whether it will be used in a given instance of your program. For example, let’s say you have

an object that is created if the user chooses a certain menu item. If the user never chooses that

NEW TERM

WARNING

84 Day 3

menu item, the object never gets created. So far, so good. The problem is that you need to

delete the pointer if the object was created, but not delete the pointer if the object was not

created. Deleting an initialized pointer is asking for trouble because you have no idea what

memory the pointer points to. There are two ways to work around this.

I said earlier that it is a good idea to initialize pointers to 0 if you don’t use them right away.

This is a good idea for two reasons. The first reason I explained earlier (uninitialized pointers

contain random values, which is undesirable). The second reason is because it’s okay to delete

a NULL pointer—you can call delete for that pointer and not worry about whether it was ever

used:

Monster* swampThing = 0;

// later when it’s time to exit the program...

delete swampThing; // so long, sucker!

In this case you don’t really care whether memory for the object was ever allocated because

the call to delete is safe whether the pointer points to an object or is NULL.

You may run into situations where delete could be called more than

once for an object. For instance, you may create an object in one part

of your program and delete it in another part of the program. A

situation might exist where the section of code that deletes the object

might never be executed. In that case you will also want to delete the

object when the program closes (for insurance). To avoid the possibility

of a pointer getting deleted twice, get into the habit of setting the

pointer to NULL or 0 after deleting it:

Monster* borg = new Monster;

// later....

delete borg;

borg = 0;

Now, if delete is called twice for the object, it won’t matter because it’s

okay to delete a NULL pointer.

Another way around the double-delete problem is to check the pointer for a non-zero value

before calling delete:

if (swampThing) delete swampThing;

This assumes that you have been diligent in setting deleted pointers to 0 in other parts of the

program. It doesn’t matter which method you use, but be sure to use one of them in any case

where a pointer could accidentally be deleted twice.

TIP

Up to Your Neck in C++ 85

3

If you use a reference when dynamically creating an object, the syntax

for delete requires a twist. Here’s an example that illustrates this point:

MyStruct& ref = *new MyStruct;

ref.X = 100;

// later...

delete &ref;

Note that you need the address-of operator to delete the pointer in the

case of a reference. Remember that a reference cannot be set to 0, so

you must be careful not to delete a reference twice.

Another Mystery Solved

Have you figured it out yet? “Huh?” you say? The bug in the POINTER and REFERENC

programs… have you figured out what it is? You got it! The program leaks memory. I created

an array of structures allocated from the heap but never freed the memory. So what I need

is a couple of lines to clean things up just before the program ends:

getch(); // existing line

for (int i=0;i<3;i++)

delete listArray[i];

There! Now I have a properly behaving program. I just ran through the array of pointers and

deleted each one. Nuthin’ to it.

new[] and delete[]

When you call new to create an array, you are actually using the new[] version of the new

operator. It’s not important that you know the inner details of how that works, but you do

need to know how to properly delete arrays that are dynamically allocated. Earlier I gave you

an example of dynamically creating a character array. Here is the same code snippet except

with the delete[] statement added:

char buff[80];

char* bigBuff = new char[4096];

strcpy(buff, “Ricky Rat”);

strcpy(bigBuff, “Some very long string.”);

// later on...

delete[] bigBuff;

Notice that the statement calls delete[] and not just plain delete. I won’t go into a technical

description of what happens here, but this ensures that all elements in the array get properly

deleted. Be sure that if you dynamically allocate an array you call the delete[] operator to

free the memory.

NOTE

86 Day 3

HOUSE RULES: POINTERS AND DYNAMIC MEMORY ALLOCATION

n Be sure to initialize pointers to 0 if they are not used right away.

n Be sure not to delete a pointer twice.

n It is OK to delete pointers set to NULL or 0.

n Set pointers to NULL or 0 after deleting them.

n Dereference pointers to obtain the object the pointer points to.

Functions in C++

A function in C++ can do everything that a function can do in C. In addition, C++ functions

can do things that functions in C cannot. Specifically, this section looks at the following:

n Function overloading

n Default parameters

n Class member functions

n Inline functions

Function Overloading

C++ allows you to have functions that have the same name but take different parameters.

Function overloading is when you have two or more functions with the same name

but with different parameter lists.

Functions that share a common name are called overloaded functions.

On Day 1 I showed you an example program which contained a function called multiply().

Not surprisingly, this function multiplied two values together. The function took two

integers, multiplied them, and returned the result. But what if you wanted to have the

function multiply two floating-point numbers? In C you would have to have two functions:

// declarations for a program written in c

int multiplyInt(int num1, int num2);

float multiplyFloat(float num1, float num2);

short multiplyShort(short num1, short num2);

Wouldn’t it be a lot easier if you could just have a function called multiply() that would be

smart enough to know whether you wanted to multiply shorts, ints, or longs? In C++ you

NEW TERM

NEW TERM

Up to Your Neck in C++ 87

3

can create such a scenario thanks to function overloading. Here’s how the declarations for an

overloaded function look:

// declarations in C++

int multiply(int num1, int num2);

float multiply(float num1, float num2);

short multiply(short num1, short num2);

You still have to write separate functions for each of these declarations, but at least you can

use the same function name. The compiler takes care of calling the correct function based on

the parameters you pass the function. For example:

float x = 1.5;

float y = 10.5;

float result = multiply(x, y);

The compiler sees that two floats are passed to the function and calls the version of the

multiply() function that takes two floating-point values for parameters. Likewise, if two ints

are passed, the compiler calls the version of multiply() that takes two integers.

It is the parameter list that makes overloaded functions work. You can

vary either the type or the number of parameters a function takes (or

both), but you cannot create an overloaded function by changing just

the return value. For example, the following does not constitute an

overloaded function:

int DoSomething();

void DoSomething();

If you try to compile a program containing these lines, you will get a

compiler error that says, Type mismatch in redeclaration of

‘DoSomething()’. The two functions need to vary by more than just the

return value in order to have overloaded functions.

Compilers keep track of overloaded functions internally through a

process called name mangling. Name mangling means that the compiler

creates a function name that takes into account the parameter list of the

function. Internally, the compiler refers to the mangled name rather

than the plain text name you would recognize. For example, for the

multiply function taking two float values, the mangled name might be

multiply$qff.

NOTE

NOTE

88 Day 3

Let’s take a quick detour and talk about something you will need to use on occasion when

dealing with overloaded functions.

Meet the Cast

Using overloaded functions works fine as long as you use the proper data types when calling

an overloaded function. But what if you mix and match? In this case, you will need to cast

a variable or literal value.

A cast tells the compiler to temporarily treat one data type as if it were another.

A cast looks like this:

float x = (float)10 * 5.5;

In this case the cast tells the compiler, “Make the number 10 a float.” (The second number

is automatically interpreted as a float because it contains a decimal place.) Take a look at the

following code snippet:

int anInt = 5;

float aFloat = 10.5;

float result = multiply(anInt, aFloat);

In this case you will get a compiler error because there is an ambiguity between the parameters

passed and the function declarations. The compiler error, in effect, says, “I can’t figure out

from the parameters passed which version of multiply() to call.” The same error will be

produced if you use code like this:

int result = multiply(10, 10);

// is 10 a float, int or short?

Here the compiler cannot figure out whether the numeric constants are to be interpreted as

floats, ints, or shorts. When this occurs, you basically have two choices. First, you can

simply avoid using literal values in the function call. If you want to multiply two ints, you

can declare two int variables and pass those to the function:

int x = 10;

int y = 10;

int result = multiply(x, y);

Now there is no ambiguity because x and y are both obviously ints. That’s probably overkill

for simple situations, though. The other thing you can do is to cast the numeric constants

to tell the compiler what type to expect:

int result = multiply((int)10, (int)10);

NEW TERM

Up to Your Neck in C++ 89

3

Now the compiler knows to treat the literal values as ints. A cast is also used to temporarily

force the compiler to treat one data type as if it were something else. Let’s go back to the first

example in this section and this time cast one of the variables to remove the ambiguity:

int x = 5;

float y = 10.5;

float result = multiply((float)x, y);

In this case x is an int, but you are casting it to a float, thereby telling the compiler to treat

it as a float. The compiler happily calls the float version of multiply() and goes on its way.

Ultimately, you want to write overloaded functions so that ambiguities do not exist and

casting is not necessary. In some cases that is not possible, and in those cases casting will be

required.

Default Parameters for Functions

A function in C++ can have default parameters which, as the name implies, supply

a default value for a function if no value is specified when the function is called.

A function implementing a default parameter might look like this:

// declaration, parameter ‘eraseFirst’ will be false by default

void Redraw(bool eraseFirst = false);

// definition

void Redraw(bool eraseFirst)

{

if (eraseFirst) {

// erase code

}

// drawing code

}

When this function is called, it can be called with or without a parameter. If the parameter

is supplied at the time the function is called, the function behaves as a regular function would.

If the parameter is not supplied when the function is called, the default parameter is used

automatically. Given this example, the following two lines of code are identical:

Redraw();

Redraw(false);

Note that when a parameter has a default value, it can be omitted from the function call

altogether. You can mix default and non-default parameters in the same function:

int PlaySound(char* name, bool loop = false, int loops = 10);

// call function

int res;

res = PlaySound(“chime.wav”); // does not loop sound

res = PlaySound(“ding.wav”, true); // plays sound 10 times

res = PlaySound(“bell.wave”, true, 5); // plays sound 5 times

NEW TERM

90 Day 3

Default parameters are helpful for many reasons. For one thing, they make your life easier.

You may have a function that you call with the same parameters 99 percent of the time. By

giving it default parameters, you shorten the amount of typing required each time you make

a call to the function. Whenever you want to supply parameters other than the defaults, all

you have to do is plug in values for the default parameters.

Any default parameters must come at the end of the function’s parameter

list. The following is not a valid function declaration:

int MyFunction(int x, int y = 10, int t = 5, int z);

In order for this function declaration to compile, the default parameters

must be moved to the end of the function list:

int MyFunction(int x, int z, int y = 10, int t = 5);

If you don’t put the default parameters at the end of the parameter list,

the compile will generate a compiler error.

Class Member Functions

As you will find out in this section, classes can contain their own functions. Such

functions are called member functions because they are members of a class.

Class member functions follow the same rules as regular functions: They can be overloaded,

they can have default parameters, they can take any number of parameters, and so on.

Class member functions can be called only through an object of the class to which the

function belongs. To call a class member function, you use the direct member operator (in

the case of local objects) or the indirect member operator (for dynamically created objects)

just like you did when accessing data members of a structure on Day 2. For example, let’s say

that you had a class called Airplane that was used to track an airplane for aircraft-control

software. That class would probably have the capability to retrieve the current speed of a given

aircraft via a function called GetSpeed(). The following example illustrates how you would

call the GetSpeed() function of an Airplane object:

Airplane plane; // create a class instance

int speed = plane.GetSpeed();

cout << “The airplane’s current speed is “ <<>

This code uses the direct membership operator to call the GetSpeed() function. Class member

functions are defined like regular functions except that the class name and scope-resolution

operator precede the function name. For example, the definition of the GetSpeed() function

might look like this in the source file:

NOTE

NEW TERM

Up to Your Neck in C++ 91

3

int Airplane::GetSpeed()

{

return speed; // speed is a class member variable

}

In this case, the scope-resolution operator tells the compiler that the GetSpeed() function is

a member of the Airplane class. I’ll talk more about class member functions when I discuss

classes tomorrow.

Tradition has it that class member function names begin with uppercase

letters. There is no hard and fast rule about this, but you will find

that most C++ programs follow this tradition. As a further note, I am

not a fan of the underscore character in function names. For example, I

much prefer the function name GetVideoRect() over the name

get_video_rect(). Regardless of what naming convention you use for

your functions, be consistent and use the same naming convention

throughout your programs.

Inline Functions

Normally a function only appears in the executable file once. Each section of code that uses

the function calls the function. This means that program execution jumps from the point

of the function call to the point in the program where the function resides. The statements

in the function are executed, and then the function returns. When the function returns,

program execution jumps back to the statement following the function call.

An inline function, as its name implies, is placed inline in the compiled code wherever

a call to that function occurs.

Inline functions are declared like regular functions but are defined with the inline keyword.

Each time the compiler encounters a call to an inline function in the source code, it places

a separate copy of the function’s code in the executable program at that point. Inline

functions execute quickly because no actual function call takes place (the code is already

inlined in the program).

Inline functions should be reserved for functions that are very small or

for those that need to be executed very quickly. Large functions or

those that are called from many places in your program should not be

inlined because your executable file will be larger as a result.

NOTE

NOTE

NEW TERM

92 Day 3

Inline functions are usually class member functions. Often the inline function definition (the

function itself) is placed in the header file following the class declaration. (This is the one time

that you can place code in your header files.) Because the GetSpeed() function mentioned

previously is so small, it can be inlined easily. Here’s how it would look:

inline int Airplane::GetSpeed() {

return speed; // speed is a class member variable

}

An inline function can also be defined within a class declaration. Because I haven’t talked

about classes yet, though, I’ll hold that discussion for tomorrow.

Summary

Wow, that’s some pretty heavy stuff ! Because you are reading this, you must still be left

standing. That’s good news. Today we got out the big guns and took on pointers and

references. Once you get a handle on pointers, you are well on your way to understanding

C++. As part of the discussion on pointers you learned about local versus dynamic memory

allocation, which led to a discussion about the new and delete operators. Today ends with

an explanation of how C++ extends the use of functions over what the C language provides.

Workshop

The Workshop contains quiz questions to help you solidify your understanding of the

material covered and exercises to provide you with experience in using what you have learned.

You can find answers to the quiz questions in Appendix A, “Answers to Quiz Questions.”

Q&A

Q Pointers and references confuse me. Am I alone?

A Absolutely not! Pointers and references are complicated and take some time to fully

understand. You will probably have to work with C++ a while before you get a

handle on pointers and references.

Q Do I always have to delete an object that I created dynamically with the new

operator?

A Yes and no. All objects created with new must have a corresponding delete, or the

program will leak memory. Some objects, however, have parent objects that will

take the responsibility for deleting them. So the question is not whether an object

created with new should be deleted, but rather who should delete it. You will always

want to call delete for classes you write. Later, when you learn about VCL (on

Up to Your Neck in C++ 93

3

Day 5, “C++ Class Frameworks and the Visual Component Model”), you will see

that VCL parent objects take the responsibility for deleting their children.

Q Should I create my objects on the stack or on the heap?

A That depends on the object. Large objects should be created on the heap in order

to preserve stack space. Small objects and primitive data types should be created on

the stack for simplicity and speed of execution.

Q What’s the point of having overloaded functions?

A Overloaded functions provide you a means by which you can have several functions

that perform the same basic operation and have the same function name, but take

different parameters. For example, you might have an overloaded function called

DrawObject(). One version might take a Circle class as a parameter, another might

take a Square class as a parameter, and a third could take a class called Polygon as a

parameter. By having three functions with the same name, you avoid the need to

have three different function names.

Q Should I use a lot of inline functions?

A That depends on the function, of course. In general, though, the answer is no.

Inline functions should be reserved for functions that are very small or seldom

used, or where execution speed is critical.

Quiz

1. What is a pointer?

2. What does it mean to dereference a pointer?

3. What is the return value of operator new?

4. Should instances of classes and structures be passed to functions by reference or by

value?

5. What does the const keyword do?

6. Does the following qualify as an overloaded function? Why or why not?

void MyFunction(int x);

long MyFunction(int x);

7. Which is better to use, a reference or a pointer?

8. What is a class member function?

9. How does the compiler treat an inline function as opposed to a regular function?

10. What, if anything, is wrong with the following code snippet?

char* buff = new char[200];

// later...

delete buff;

94 Day 3

Exercises

1. Write a program that declares a structure, dynamically creates an instance of the

structure, and fills the structure with data. (Hint: Don’t forget to delete the

pointer.)

2. Modify the program from Exercise 1 to use a reference rather than a pointer.

3. Rewrite the REFERENC program in Listing 3.2 so that the mailingListRecord

structure is passed to the displayRecord() function by reference rather than by

value.

4. What is wrong with the following function declaration?

void SomeFunction(int param1, int param2 = 0, int param3);

5. Explain to a five-year-old the difference between pointers and references.

Totally Immersed: C++ Classes and Object-Oriented Programming 95