📄 apf.htm
字号:
name of the class you're using, followed by the name of the object. For example,
the List application declares its list like this:</P>
<P>
<PRE>CPtrList list;
</PRE>
<P>Here, the program is declaring an object of the CPtrList class. This class holds
a linked list of pointers, which means that the list can reference nearly any type
of information.</P>
<P>Although there's not much you need to do to initialize an empty list, you do need
to decide what type of information will be pointed to by the pointers in the list.
That is, you need to declare exactly what a node in the list will look like. The
List application declares a node as shown in Listing F.4.</P>
<P>
<H4>Listing F.4  CNode Structure</H4>
<PRE>struct CNode
{
int value1;
int value2;
</PRE>
<PRE>};
</PRE>
<P>Here, a node is defined as a structure holding two integer values. However, you
can create any type of data structure you like for your nodes. To add a node to a
list, you use the new operator to create a node structure in memory, and then you
add the returned pointer to the pointer list. The List application begins its list
with a single node, which is created in the view class's constructor, as shown in
Listing F.5.</P>
<P>
<H4>Listing F.5  CMyListView Constructor</H4>
<PRE>CMyListView::CMyListView()
{
CNode* pNode = new CNode;
pNode->value1 = 11;
pNode->value2 = 22;
list.AddTail(pNode);
</PRE>
<PRE>}
</PRE>
<P>In Listing F.5, the program first creates a new CNode structure on the heap and
then sets the node's two members. After initializing the new node, a quick call to
the list's AddTail() member function adds the node to the list. Because the list
was empty, adding a node to the tail of the list is the same as adding the node to
the head of the list. That is, the program could have also called AddHead() to add
the node. In either case, the new single node is now both the head and tail of the
list.</P>
<P>
<H3><A NAME="Heading10"></A>Adding a Node to the List</H3>
<P>Although you can insert nodes at any position in a list, the easiest way to add
to a list is to add a node to the head or tail, making the node the new head or tail.
In the List application, you left-click in the window to bring up the Add Node dialog
box, so you'll want to examine the OnLButtonDown() function, which looks like Listing
F.6.</P>
<P>
<H4>Listing F.6  CMyListView::OnLButtonDown()</H4>
<PRE>void CMyListView::OnLButtonDown(UINT nFlags, CPoint point)
{
// Create and initialize the dialog box.
AddNodeDlg dialog;
dialog.m_value1 = 0;
dialog.m_value2 = 0;
// Display the dialog box.
int result = dialog.DoModal();
// If the user clicked the OK button...
if (result == IDOK)
{
// Create and initialize the new node.
CNode* pNode = new CNode;
pNode->value1 = dialog.m_value1;
pNode->value2 = dialog.m_value2;
// Add the node to the list.
list.AddTail(pNode);
// Repaint the window.
Invalidate();
}
CView::OnLButtonDown(nFlags, point);
</PRE>
<PRE>}
</PRE>
<P>In Listing F.6, after displaying the dialog box, the program checks whether the
user exited the dialog with the OK button. If so, the user wants to add a new node
to the list. In this case, the program creates and initializes the new node, as it
did previously for the first node that it added in the view class's constructor.
The program adds the node in the same way, too, by calling the AddTail(). If you
want to modify the List application, one thing you could try is to give the user
a choice between adding the node at the head or the tail of the list, instead of
just at the tail.</P>
<P>
<H3><A NAME="Heading11"></A>Deleting a Node from the List</H3>
<P>Deleting a node from a list can be easy or complicated, depending on where in
the list you want to delete the node. As with adding a node, dealing with nodes other
than the head or tail requires that you first locate the node that you want and then
get its position in the list. You'll learn about node positions in the next section,
which demonstrates how to iterate over a list. To keep things simple, however, this
program enables you to delete nodes only from the head or tail of the list, as shown
in Listing F.7.</P>
<P>
<H4>Listing F.7  CMyListView::OnRButtonDown()</H4>
<PRE>void CMyListView::OnRButtonDown(UINT nFlags, CPoint point)
{
// Create and initialize the dialog box.
RemoveNodeDlg dialog;
dialog.m_radio = 0;
// Display the dialog box.
int result = dialog.DoModal();
// If the user clicked the OK button...
if (result == IDOK)
{
CNode* pNode;
// Make sure the list isn't empty.
if (list.IsEmpty())
MessageBox("No nodes to delete.");
else
{
// Remove the specified node.
if (dialog.m_radio == 0)
pNode = (CNode*)list.RemoveHead();
else
pNode = (CNode*)list.RemoveTail();
// Delete the node object and repaint the window.
delete pNode;
Invalidate();
}
}
CView::OnRButtonDown(nFlags, point);
</PRE>
<PRE>}
</PRE>
<P>Here, after displaying the dialog box, the program checks whether the user exited
the dialog box via the OK button. If so, the program must then check whether the
user wants to delete a node from the head or tail of the list. If the Remove Head
radio button was checked, the dialog box's m_radio data member will be 0. In this
case, the program calls the list class's RemoveHead() member function. Otherwise,
the program calls RemoveTail(). Both of these functions return a pointer to the object
that was removed from the list. Before calling either of these member functions,
however, notice how the program calls IsEmpty() to determine whether the list contains
any nodes. You can't delete a node from an empty list.</P>
<BLOCKQUOTE>
<P>
<HR>
<STRONG>NOTE:</STRONG>otice that when removing a node from the list, the List application
calls delete on the pointer returned by the list. It's important to remember that
when you remove a node from a list, the node's pointer is removed from the list,
but the object to which the pointer points is still in memory, where it stays until
you delete it. n
<HR>
</BLOCKQUOTE>
<H3><A NAME="Heading12"></A>Iterating Over the List</H3>
<P>Often, you'll want to <I>iterate over</I> (read through) a list. For example,
you might want to display the values in each node of the list, starting from the
head of the list and working your way to the tail. The List application does exactly
this in its OnDraw() function, as shown in Listing F.8.</P>
<P>
<H4>Listing F.8  CMyListView::OnDraw()</H4>
<PRE>void CMyListView::OnDraw(CDC* pDC)
{
CListDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// Get the current font's height.
TEXTMETRIC textMetric;
pDC->GetTextMetrics(&textMetric);
int fontHeight = textMetric.tmHeight;
// Initialize values used in the loop.
POSITION pos = list.GetHeadPosition();
int displayPosition = 10;
int index = 0;
// Iterate over the list, displaying each node's values.
while (pos != NULL)
{
CNode* pNode = (CNode*)list.GetNext(pos);
char s[81];
wsprintf(s, "Node %d contains %d and %d.",
index, pNode->value1, pNode->value2);
pDC->TextOut(10, displayPosition, s);
displayPosition += fontHeight;
++index;
}
</PRE>
<PRE>}
</PRE>
<P>In Listing F.8, the program gets the position of the head node by calling the
GetHeadPosition() member function. The position is a value that many of the list
class's member functions use to quickly locate nodes in the list. You must have this
starting position value to iterate over the list.</P>
<P>In the while loop, the iteration actually takes place. The program calls the list
object's GetNext() member function, which requires as its single argument the position
of the node to retrieve. The function returns a pointer to the node and sets the
position to the next node in the list. When the position is NULL, the program has
reached the end of the list. In Listing F.8, this NULL value is the condition that's
used to terminate the while loop.</P>
<P>
<H3><A NAME="Heading13"></A>Cleaning Up the List</H3>
<P>There's one other time when you need to iterate over a list. That's when the program
is about to terminate and you need to delete all the objects pointed to by the pointers
in the list. The List application performs this task in the view class's destructor,
as shown in Listing F.9.</P>
<P>
<H4>Listing F.9  CMyListView Destructor</H4>
<PRE>CMyListView::~CMyListView()
{
// Iterate over the list, deleting each node.
while (!list.IsEmpty())
{
CNode* pNode = (CNode*)list.RemoveHead();
delete pNode;
}
</PRE>
<PRE>}
</PRE>
<P>The destructor in Listing F.9 iterates over the list in a while loop until the
IsEmpty() member function returns TRUE. Inside the loop, the program removes the
head node from the list (which makes the next node in the list the new head) and
deletes the node from memory. When the list is empty, all the nodes that the program
allocated have been deleted.</P>
<BLOCKQUOTE>
<P>
<HR>
<STRONG>CAUTION:</STRONG><B> </B>Don't forget that you're responsible for deleting
every node that you create with the new operator. If you fail to delete nodes, you
might cause a memory leak. In a small program like this, a few wasted bytes don't
matter, but in a long-running program adding and deleting hundreds or thousands of
list nodes, you could create serious errors in your program. It's always good programming
practice to delete any objects you allocate in memory.
<HR>
</P>
<P>
<HR>
<STRONG>]TIP:</STRONG> Chapter 24, "Improving Your Application's Performance,"
discusses memory management and preventing memory leaks.
<HR>
</BLOCKQUOTE>
<H2><A NAME="Heading14"></A>The Map Classes</H2>
<P>You can use MFC's mapped collection classes for creating lookup tables. For example,
you might want to convert digits to the words that represent the numbers. That is,
you might want to use the digit 1 as a key to find the word <I>one.</I> A mapped
collection is perfect for this sort of task. Thanks to the many MFC map classes,
you can use various types of data for keys and values.</P>
<P>The MFC map classes are CMapPtrToPtr, CMapPtrToWord, CMapStringToOb, CMapStringToPtr,
CMapStringToString, CMapWordToOb, and CMapWordToPtr. The first data type in the name
is the key, and the second is the value type. For example, CMapStringToOb uses strings
as keys and objects as values, whereas CMapStringToString, which this section uses
in its examples, uses strings as both keys and values. All the map classes are similar
and so have similar member functions, which are listed and described in Table F.3.</P>
<P>
<H4>Table F.3  Functions of the Map Classes</H4>
<P>
<TABLE BORDER="1">
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT"><B>Function</B></TD>
<TD ALIGN="LEFT"><B>Description</B></TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">GetCount()</TD>
<TD ALIGN="LEFT">Gets the number of map elements</TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">GetNextAssoc()</TD>
<TD ALIGN="LEFT">Gets the next element when iterating over the map</TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">GetStartPosition()</TD>
<TD ALIGN="LEFT">Gets the first element's position</TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">IsEmpty()</TD>
<TD ALIGN="LEFT">Returns TRUE if the map is empty and returns FALSE otherwise</TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">Lookup()</TD>
<TD ALIGN="LEFT">Finds the value associated with a key</TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">RemoveAll()</TD>
<TD ALIGN="LEFT">Removes all the map's elements</TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">RemoveKey()</TD>
<TD ALIGN="LEFT">Removes an element from the map</TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">SetAt()</TD>
<TD ALIGN="LEFT">Adds a map element or replaces an element with a matching key</TD>
</TR>
</TABLE>
<BLOCKQUOTE>
<P>
<HR>
<B>Map Templates</B></P>
<P>Maps and lookup tables are another good use for templates. There are set, multiset,
map, and multimap templates in the Standard Template Library, discussed in Chapter
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -