?? ch08.htm
字號:
</BLOCKQUOTE>
<P><A NAME="Heading40"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 8.9. Creating
a stray pointer.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">
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;
<TT>23: }</TT></FONT>
<FONT COLOR="#0066FF">
Output: *pInt: 10
*pLong: 90000
*pInt: 20
*pLong: 65556
Null pointer assignment
</FONT></PRE>
<P>(Your output may look different.)</P>
<P><FONT COLOR="#000077"><B>Analysis: </B></FONT>Line 8 declares <TT>pInt</TT> to
be a pointer to <TT>USHORT</TT>, and <TT>pInt</TT> is pointed to newly allocated
memory. Line 9 puts the value <TT>10</TT> in that memory, and line 10 prints its
value. After the value is printed, <TT>delete</TT> is called on the pointer. <TT>pInt</TT>
is now a stray, or dangling, pointer.<BR>
Line 13 declares a new pointer, <TT>pLong</TT>, which is pointed at the memory allocated
by <TT>new</TT>. <BR>
<BR>
Line 14 assigns the value <TT>90000</TT> to <TT>pLong</TT>, and line 15 prints its
value.</P>
<P>Line 17 assigns the value <TT>20</TT> to the memory that <TT>pInt</TT> points
to, but <TT>pInt</TT> no longer points anywhere that is valid. The memory that <TT>pInt</TT>
points to was freed by the call to <TT>delete</TT>, so assigning a value to that
memory is certain disaster.</P>
<P>Line 19 prints the value at <TT>pInt</TT>. Sure enough, it is <TT>20</TT>. Line
20 prints <TT>20</TT>, the value at <TT>pLong</TT>; it has suddenly been changed
to <TT>65556</TT>. Two questions arise:<BR>
<BR>
<B>1.</B> How could <TT>pLong</TT>'s value change, given that <TT>pLong</TT> wasn't
touched?<BR>
<BR>
<B>2</B>. Where did the 20 go when <TT>pInt</TT> was used in line 17?<BR>
<BR>
As you might guess, these are related questions. When a value was placed at <TT>pInt</TT>
in line 17, the compiler happily placed the value <TT>20</TT> at the memory location
that <TT>pInt</TT> previously pointed to. However, because that memory was freed
in line 11, the compiler was free to reassign it. When <TT>pLong</TT> was created
in line 13, it was given <TT>pInt</TT>'s old memory location. (On some computers
this may not happen, depending on where in memory these values are stored.) When
the value <TT>20</TT> was assigned to the location that <TT>pInt</TT> previously
pointed to, it wrote over the value pointed to by <TT>pLong</TT>. This is called
"stomping on a pointer." It is often the unfortunate outcome of using a
stray pointer.<BR>
This is a particularly nasty bug, because the value that changed wasn't associated
with the stray pointer. The change to the value at <TT>pLong</TT> was a side effect
of the misuse of <TT>pInt</TT>. In a large program, this would be very difficult
to track down. <BR>
<BR>
Just for fun, here are the details of how 65,556 got into that memory address:
<DL>
<DD><B>1.</B> <TT>pInt</TT> was pointed at a particular memory location, and the
value <TT>10</TT> was assigned.<BR>
<BR>
<B>2.</B> <TT>delete</TT> was called on <TT>pInt</TT>, which told the compiler that
it could put something else at that location. Then <TT>pLong</TT> was assigned the
same memory location.<BR>
<BR>
<B>3.</B> The value <TT>90000</TT> was assigned to <TT>*pLong</TT>. 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.<BR>
<BR>
<B>4.</B> <TT>pInt</TT> was assigned the value <TT>20</TT>--or 00 14 in hexadecimal
notation. Because <TT>pInt</TT> still pointed to the same address, the first two
bytes of <TT>pLong</TT> were overwritten, leaving 00 14 00 01.<BR>
<BR>
<B>5.</B> The value at <TT>pLong</TT> was printed, reversing the bytes back to their
correct order of 00 01 00 14, which was translated into the DOS value of <TT>65556</TT>.<BR>
<BR>
<HR>
<B>DO</B> use <TT>new</TT> to create objects on the free store.<B> DO</B> use <TT>delete</TT>
to destroy objects on the free store and to return their memory. <B>DON'T</B> forget
to balance all <TT>new</TT> statements with a <TT>delete</TT> statement. <B>DON'T</B>
forget to assign <TT>null</TT> (<TT>0</TT>) to all pointers that you call <TT>delete</TT>
on. <B>DO</B> check the value returned by <TT>new</TT>.
<HR>
</DL>
<H3 ALIGN="CENTER"><A NAME="Heading42"></A><FONT COLOR="#000077">const Pointers</FONT></H3>
<P>You can use the keyword <TT>const</TT> for pointers before the type, after the
type, or in both places. For example, all of the following are legal declarations:</P>
<PRE><FONT COLOR="#0066FF">
const int * pOne;
int * const pTwo;
const int * const pThree;
</FONT></PRE>
<P><TT>pOne</TT> is a pointer to a constant integer. The value that is pointed to
can't be changed.</P>
<P><TT>pTwo</TT> is a constant pointer to an integer. The integer can be changed,
but <TT>pTwo</TT> can't point to anything else.</P>
<P><TT>pThree</TT> is a constant pointer to a constant integer. The value that is
pointed to can't be changed, and <TT>pThree</TT> can't be changed to point to anything
else.</P>
<P>The trick to keeping this straight is to look to the right of the keyword <TT>const</TT>
to find out what is being declared constant. If the type is to the right of the keyword,
it is the value that is constant. If the variable is to the right of the keyword
<TT>const</TT>, it is the pointer variable itself that is constant.</P>
<PRE><FONT COLOR="#0066FF">const int * p1; // the int pointed to is constant
int * const p2; // p2 is constant, it can't point to anything else
</FONT></PRE>
<H4 ALIGN="CENTER"><A NAME="Heading43"></A><FONT COLOR="#000077">const Pointers and
const Member Functions</FONT></H4>
<P>On Day 6, "Basic Classes," you learned that you can apply the keyword
<TT>const</TT> to a member function. When a function is declared <TT>const</TT>,
the compiler flags as an error any attempt to change data in the object from within
that function.</P>
<P>If you declare a pointer to a <TT>const</TT> object, the only methods that you
can call with that pointer are <TT>const</TT> methods. Listing 8.10 illustrates this.</P>
<P><A NAME="Heading44"></A><FONT SIZE="4" COLOR="#000077"><B>Listing 8.10. Using
pointers to const objects.</B></FONT></P>
<PRE><FONT COLOR="#0066FF">
1: // Listing 8.10
2: // Using pointers with const methods
3:
4: #include <iostream.h>
5:
6: class Rectangle
7: {
8: public:
9: Rectangle();
10: ~Rectangle();
11: void SetLength(int length) { itsLength = length; }
12: int GetLength() const { return 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: itsWidth(5),
24: itsLength(10)
25: {}
26:
27: Rectangle::~Rectangle()
28: {}
29:
30: int main()
31: {
32: Rectangle* pRect = new Rectangle;
33: const Rectangle * pConstRect = new Rectangle;
34: Rectangle * const pConstPtr = new Rectangle;
35:
36: cout << "pRect width: " << pRect->GetWidth() << " feet\n";
37: cout << "pConstRect width: " << pConstRect->GetWidth() << " feet\n";
38: cout << "pConstPtr width: " << pConstPtr->GetWidth() << " feet\n";
39:
40: pRect->SetWidth(10);
41: // pConstRect->SetWidth(10);
42: pConstPtr->SetWidth(10);
43:
44: cout << "pRect width: " << pRect->GetWidth() << " feet\n";
45: cout << "pConstRect width: " << pConstRect->GetWidth() << " feet\n";
46: cout << "pConstPtr width: " << pConstPtr->GetWidth() << " feet\n";
47: return 0;
<TT>48: }</TT></FONT>
<FONT COLOR="#0066FF">
Output: pRect width: 5 feet
pConstRect width: 5 feet
pConstPtr width: 5 feet
pRect width: 10 feet
pConstRect width: 5 feet
pConstPtr width: 10 feet
</FONT></PRE>
<P><FONT COLOR="#000077"><B>Analysis: </B></FONT>Lines 6-20 declare <TT>Rectangle</TT>.
Line 15 declares the <TT>GetWidth()</TT> member method <TT>const</TT>. Line 32 declares
a pointer to <TT>Rectangle</TT>. Line 33 declares <TT>pConstRect</TT>, which is a
pointer to a constant <TT>Rectangle</TT>. Line 34 declares <TT>pConstPtr</TT>, which
is a constant pointer to <TT>Rectangle</TT>.<BR>
Lines 36-38 print their values.</P>
<P>In line 40, <TT>pRect</TT> is used to set the width of the rectangle to <TT>10</TT>.
In line 41, <TT>pConstRect</TT> would be used, but it was declared to point to a
constant <TT>Rectangle</TT>. Therefore, it cannot legally call a non-<TT>const</TT>
member function; it is commented out. In line 38, <TT>pConstPtr</TT> calls <TT>SetAge()</TT>.
<TT>pConstPtr</TT> is declared to be a constant pointer to a rectangle. In other
words, the pointer is constant and cannot point to anything else, but the rectangle
is not constant.
<H4 ALIGN="CENTER"><A NAME="Heading46"></A><FONT COLOR="#000077">const this Pointers</FONT></H4>
<P>When you declare an object to be <TT>const</TT>, you are in effect declaring that
the <TT>this</TT> pointer is a pointer to a <TT>const</TT> object. A <TT>const</TT>
<TT>this</TT> pointer can be used only with <TT>const</TT> mem- ber functions.</P>
<P>Constant objects and constant pointers will be discussed again tomorrow, when
references to constant objects are discussed.
<BLOCKQUOTE>
<P>
<HR>
<B>DO</B> protect objects passed by reference with <TT>const</TT> if they should
not be changed. <B>DO </B>pass by reference when the object can be changed.<B> DO</B>
pass by value when small objects should not be changed.
<HR>
</BLOCKQUOTE>
<H3 ALIGN="CENTER"><A NAME="Heading47"></A><FONT COLOR="#000077">Summary</FONT></H3>
<P>Pointers provide a powerful way to access data by indirection. Every variable
has an address, which can be obtained using the <TT>address of</TT> operator (<TT>&</TT>).
The address can be stored in a pointer.</P>
<P>Pointers are declared by writing the type of object that they point to, followed
by the indirection operator (<TT>*</TT>) and the name of the pointer. Pointers should
be initialized to point to an object or to <TT>null</TT> (<TT>0</TT>).</P>
<P>You access the value at the address stored in a pointer by using the indirection
operator (<TT>*</TT>). You can declare <TT>const</TT> pointers, which can't be reassigned
to point to other objects, and pointers to <TT>const</TT> objects, which can't be
used to change the objects to which they point.</P>
<P>To create new objects on the free store, you use the <TT>new</TT> keyword and
assign the address that is returned to a pointer. You free that memory by calling
the <TT>delete</TT> keyword on the pointer. <TT>delete</TT> frees the memory, but
it doesn't destroy the pointer. Therefore, you must reassign the pointer after its
memory has been freed.
<H3 ALIGN="CENTER"><A NAME="Heading48"></A><FONT COLOR="#000077">Q&A</FONT></H3>
<DL>
<DD><B>Q. Why are pointers so important?<BR>
</B><BR>
<B>A.</B> Today you saw how pointers are used to hold the address of objects on the
free store and how they are used to pass arguments by reference. In addition, on
Day 13, "Polymorphism," you'll see how pointers are used in class polymorphism.<BR>
<BR>
<B>Q. Why should I bother to declare anything on the free store?<BR>
</B><BR>
<B>A.</B> Objects on the free store persist after the return of a function. Additionally,
the ability to store objects on the free store enables you to decide at runtime how
many objects you need, instead of having to declare this in advance. This is explored
in greater depth tomorrow.<BR>
<BR>
<B>Q. Why should I declare an object const if it limits what I can do with it?<BR>
</B><BR>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -