|
Every
class member function has a hidden parameter: the
this pointer. this points to the individual object.
Therefore, in each call to GetAge() or SetAge(),
the this pointer for the object is included as a
hidden parameter.
It
is possible to use the this pointer explicitly,
as Listing 8.8 illustrates.
Listing
8.8. Using the this pointer.
1:
// Listing 8.8
2:
// Using the this pointer
3:
4:
#include <iostream.h>
5:
6:
class Rectangle
7:
{
8:
public:
9:
Rectangle();
10:
~Rectangle();
11:
void SetLength(int length) { this->itsLength
= length; }
12:
int GetLength() const { return this->itsLength;
}
13:
14:
void SetWidth(int width) { itsWidth = width; }
15:
int GetWidth() const { return itsWidth; }
16:
17:
private:
18:
int itsLength;
19:
int itsWidth;
20:
};
21:
22:
Rectangle::Rectangle()
23:
{
24:
itsWidth = 5;
25:
itsLength = 10;
26:
}
27:
Rectangle::~Rectangle()
28:
{}
29:
30:
int main()
31:
{
32:
Rectangle theRect;
33:
cout << "theRect is " << theRect.GetLength()
<< " feet long.\n";
34:
cout << "theRect is " << theRect.GetWidth()
<< " feet wide.\n";
35:
theRect.SetLength(20);
36:
theRect.SetWidth(10);
37:
cout << "theRect is " << theRect.GetLength()<<
" feet long.\n";
38:
cout << "theRect is " << theRect.GetWidth()<<
" feet wide.\n";
39:
return 0;
40:
}
OUTPUT:
theRect is 10 feet long.
theRect is 5 feet long.
theRect is 20 feet long.
theRect is 10 feet long.
ANALYSIS: The SetLength() and GetLength()
accessor functions explicitly use the this pointer
to access the member variables of the Rectangle
object. The SetWidth and GetWidth accessors do not.
There is no difference in their behavior, although
the syntax is easier to understand.
If that were all there was to the this pointer,
there would be little point in bothering you with
it. The this pointer, however, is a pointer; it
stores the memory address of an object. As such,
it can be a powerful tool.
You’ll
see a practical use for the this pointer in Chapter10,
"Advanced Functions," when operator overloading
is discussed. For now, your goal is to know about
the this pointer and to understand what it is: a
pointer to the object itself.
You
don’t have to worry about creating or deleting the
this pointer. The compiler takes care of that.
Stray
or Dangling Pointers
One
source of bugs that are nasty and difficult to find
is stray pointers. A stray pointer is created when
you call delete on a pointer—thereby freeing the
memory that it points to—and later try to use that
pointer again without reassigning it.
It
is as though the Acme Mail Order company moved away,
and you still pressed the programmed button on your
phone. It is possible that nothing terrible happens—a
telephone rings in a deserted warehouse. Perhaps
the telephone number has been reassigned to a munitions
factory, and your call detonates an explosive and
blows up your whole city!
In
short, be careful not to use a pointer after you
have called delete on it. The pointer still points
to the old area of memory, but the compiler is free
to put other data there; using the pointer can cause
your program to crash. Worse, your program might
proceed merrily on its way and crash several minutes
later. This is called a time bomb, and it is no
fun. To be safe, after you delete a pointer, set
it to null (0). This disarms the pointer.
NOTE: Stray pointers are often called
wild pointers or dangling pointers.
Listing
8.9 illustrates creating a stray pointer.
WARNING: This program intentionally
creates a stray pointer. Do NOT run this program—it
will crash, if you are lucky.
Listing
8.9. Creating a stray pointer.
1:
// Listing 8.9
2:
// Demonstrates a stray pointer
3:
typedef unsigned short int USHORT;
4:
#include <iostream.h>
5:
6:
int main()
7:
{
8:
USHORT * pInt = new USHORT;
9:
*pInt = 10;
10:
cout << "*pInt: " << *pInt <<
endl;
11:
delete pInt;
12:
pInt = 0;
13:
long * pLong = new long;
14:
*pLong = 90000;
15:
cout << "*pLong: " << *pLong <<
endl;
16:
17:
*pInt = 20; // uh oh, this was deleted!
18:
19:
cout << "*pInt: " << *pInt <<
endl;
20:
cout << "*pLong: " << *pLong <<
endl;
21:
delete pLong;
22:
return 0;
23:
}
OUTPUT:
*pInt: 10
*pLong: 90000
*pInt: 20
*pLong: 65556
Null pointer assignment
(Your
output may look different.)
ANALYSIS : Line 8 declares pInt to
be a pointer to USHORT, and pInt is pointed to newly
allocated memory. Line 9 puts the value 10 in that
memory, and line 10 prints its value. After the
value is printed, delete is called on the pointer.
pInt is now a stray, or dangling, pointer.
Line 13 declares a new pointer, pLong, which is
pointed at the memory allocated by new.
Line 14 assigns the value 90000 to pLong, and line
15 prints its value.
Line
17 assigns the value 20 to the memory that pInt
points to, but pInt no longer points anywhere that
is valid. The memory that pInt points to was freed
by the call to delete, so assigning a value to that
memory is certain disaster.
Line
19 prints the value at pInt. Sure enough, it is
20. Line 20 prints 20, the value at pLong; it has
suddenly been changed to 65556. Two questions arise:
1.
How could pLong’s value change, given that pLong
wasn’t touched?
2.
Where did the 20 go when pInt was used in line 17?
As
you might guess, these are related questions. When
a value was placed at pInt in line 17, the compiler
happily placed the value 20 at the memory location
that pInt previously pointed to. However, because
that memory was freed in line 11, the compiler was
free to reassign it. When pLong was created in line
13, it was given pInt’s old memory location. (On
some computers this may not happen, depending on
where in memory these values are stored.) When the
value 20 was assigned to the location that pInt
previously pointed to, it wrote over the value pointed
to by pLong. This is called "stomping on a pointer."
It is often the unfortunate outcome of using a stray
pointer.
This
is a particularly nasty bug, because the value that
changed wasn’t associated with the stray pointer.
The change to the value at pLong was a side effect
of the misuse of pInt. In a large program, this
would be very difficult to track down.
Just
for fun, here are the details of how 65,556 got
into that memory address:
1.
pInt was pointed at a particular memory location,
and the value 10 was assigned.
2.
delete was called on pInt, which told the compiler
that it could put something else at that location.
Then pLong was assigned the same memory location.
3.
The value 90000 was assigned to *pLong. The particular
computer used in this example stored the four-byte
value of 90,000 (00 01 5F 90) in byte-swapped order.
Therefore, it was stored as 5F 90 00 01.
4.
pInt was assigned the value 20—or 00 14 in hexadecimal
notation. Because pInt still pointed to the same
address, the first two bytes of pLong were overwritten,
leaving 00 14 00 01.
5.
The value at pLong was printed, reversing the bytes
back to their correct order of 00 01 00 14, which
was translated into the DOS value of 65556.
DO
use new to create objects on the free store.
DO use delete to destroy objects on the free
store and to return their memory. DON’T forget
to balance all new statements with a delete statement.
DON’T forget to assign null (0) to all pointers
that you call delete on. DO check the value
returned by new.
|