MFC 的CList,CPtrList,CObList,CStringList 的用法

一直在编程中使用stl进行编程,但是最近感觉微软提供的模版类也是很好,出错机率更低,建议在mfc的工程使用。相关介绍如下,参照msdn搞清接口即可使用

CList是通用型的列表类,你可以存放指定的数据类型,用法如下:
CList<CPoint, CPoint&> list;
这样就指定了CList中存放的是CPoint类型的引用;

CPtrList,CObList,CStringList都是具体的用于某种类型的集合类型
而CPtrList存放的是void类型的指针,取出的时候必须要强制转换;
CObList用于存放从CObject派生的数据类型的指针;
CStringList存放CString字符串类型,相当于CList<Cstring,CString>

///////////////////////////////////////////////////////////////////

Lists
The InsertAt and RemoveAt functions make it easy to add items to an array and to take them away. But the ease with which items are inserted and removed comes at a cost: when items are inserted or removed in the middle of an array, items higher in the array must be shifted upward or downward in memory. The performance penalty incurred when manipulating large arrays in this manner can be quite expensive.

A classic solution to the problem of maintaining ordered lists that support fast item insertion and removal is the linked list. A linked list is a collection of items that contain pointers to other items. In a singly linked list, each item contains a pointer to the next item in the list. Moving forward through a singly linked list is fast because moving to the next item is a simple matter of extracting that item's address from the current item. To support fast forward and backward traversal, many lists are doubly linked—that is, each item contains a pointer to the previous item in the list as well as to the next item. Given the address of the first item in the list (the head), it's a simple matter to enumerate the items in the list using code like this:

item* pItem = GetHead ();
while (pItem != NULL)
    pItem = pItem->pNextItem;


Conversely, given the address of the final item in the list (the tail), a doubly linked list can be traversed in reverse order, like this:

item* pItem = GetTail ();
while (pItem != NULL)
    pItem = pItem->pPrevItem;


These examples assume that the list doesn't wrap around on itself—that is, that the pNextItem pointer in the final item and the pPrevItem pointer in the first item are equal to NULL. Some linked lists form a circular chain of items by connecting the first and last items.

How do linked lists solve the problem of fast item insertion and removal? Inserting an item midway through the list doesn't require any items to be shifted upward in memory; it simply requires that the pointers stored in the items before and after the insertion point be adjusted to reference the new item. Removing an item is equally efficient, requiring nothing more than the adjustment of two pointers. Compare this to inserting an item into the middle of an array, which could require a memcpy involving tens, hundreds, or perhaps thousands of items to make room for one new item, and the benefits should be obvious.

Nearly every programmer has, at some point in his or her career, implemented a linked list. Everyone should do it once, but no one should have to do it more than once. Fortunately, many class libraries, including MFC, provide canned implementations of linked lists. As an MFC programmer, you can sleep well tonight knowing that you'll probably never have to write a linked list from scratch again.


The MFC List Classes
The MFC template class CList implements a generic linked list that can be customized to work with any data type. MFC also provides the following nontemplatized list classes to deal with specific data types. These classes are provided primarily for compatibility with older versions of MFC and aren't used very often in modern MFC applications.

Type-Specific MFC List Classes

Class Name Data Type
CObList CObject pointers
CPtrList void pointers
CStringList CStrings

MFC lists are doubly linked for fast forward and backward traversal. Positions in the list are identified by abstract values called POSITIONs. For a list, a POSITION is actually a pointer to a CNode data structure representing one item in the list. CNode contains three fields: a pointer to the next CNode structure in the list, a pointer to the previous CNode structure, and a pointer to the item data. Insertions at the head of the list, the tail, or at a specified POSITION are fast and efficient. Lists can also be searched, but because searches are performed by traversing the list sequentially and examining its items one by one, they can be time-consuming if the list is long.

I'll use CStringList to demonstrate how the list classes are used, but keep in mind that the principles demonstrated here apply to the other list classes as well. The following example creates a CStringList object and adds 10 strings to it:

// Schools of the Southeastern Conference
const TCHAR szSchools[][20] = {
    _T ("Alabama"),
    _T ("Arkansas"),
    _T ("Florida"),
    _T ("Georgia"),
    _T ("Kentucky"),
    _T ("Mississippi"),
    _T ("Mississippi State"),
    _T ("South Carolina"),
    _T ("Tennessee"),
    _T ("Vanderbilt"),
};

CStringList list;
for (int i=0; i<10; i++)
    list.AddTail (szSchools[i]);


The AddTail function adds an item (or all the items in another linked list) to the end of the list. To add items to the head of the list, use the AddHead function instead. Removing an item from the head or tail is as simple as calling RemoveHead or RemoveTail. The RemoveAll function removes all the items in one fell swoop.

Each time a string is added to a CStringList, MFC copies the string to a CString and stores it in the corresponding CNode structure. Therefore, it's perfectly acceptable to allow the strings that you initialize a list with to go out of scope once the list is built.

Once a list is created, you can iterate through it forward and backward using the GetNext and GetPrev functions. Both accept a POSITION value identifying the current position in the list and return the item at that position. Each also updates the POSITION value to reference the next or previous item. You can retrieve the POSITION of the first or last item in the list with GetHeadPosition or GetTailPosition. The following statements enumerate the items in the list from first to last, writing each string retrieved from the list to the debug output window using MFC's TRACE macro:

POSITION pos = list.GetHeadPosition ();
while (pos != NULL) {
    CString string = list.GetNext (pos);
    TRACE (_T ("%s/n"), string);
}

 


Walking the list backward is equally simple:

POSITION pos = list.GetTailPosition ();
while (pos != NULL) {
    CString string = list.GetPrev (pos);
    TRACE (_T ("%s/n"), string);
}

 


If you simply want to retrieve the first or last item in the list, you can use the list's GetHead or GetTail function. Neither requires a POSITION value as input because the position is implied in the call.

Given a POSITION value pos identifying a particular item, you can use the list's At functions to retrieve, modify, or delete the item:

CString string = list.GetAt (pos);       // Retrieve the item.
list.SetAt (pos, _T ("Florida State"));  // Change it.
list.RemoveAt (pos);                     // Delete it.

 


You can also use InsertBefore or InsertAfter to insert items into the list:

list.InsertBefore (pos, _T ("Florida State"));  // Insert at pos.
list.InsertAfter (pos, _T ("Florida State"));   // Insert after pos.

 


Because of the nature of linked lists, insertions and removals performed this way are fast.

MFC's list classes include two member functions that you can use to perform searches. FindIndex accepts a 0-based index and returns the POSITION of the item at the corresponding location in the list. Find searches the list for an item matching an input you specify and returns its POSITION. For string lists, Find compares strings. For pointer lists, it compares pointers; it does not dereference the pointers and compare the items that they point to. Searching a string list for "Tennessee" requires just one function call:

POSITION pos = list.Find (_T ("Tennessee"));

 


By default, Find searches the list from beginning to end. If you'd like, you can specify an alternate starting point in the function's second parameter. But be aware that if the item you're looking for occurs before the starting POSITION, Find won't find it because searches don't wrap around to the beginning of the list.

You can find out how many elements a list contains with the GetCount function. If GetCount returns 0, the list is empty. A quick way to test for an empty list is to call IsEmpty.

Creating Type-Safe List Classes with CList
You can create type-safe list classes for the data types of your choice from MFC's CList class. Here's an example involving a linked list of CPoint objects:

CList<CPoint, CPoint&> list;

// Populate the list.
for (int i=0; i<10; i++)
    list.AddTail (CPoint (i*10, 0));

// Enumerate the items in the list.
POSITION pos = list.GetHeadPosition ();
while (pos != NULL) {
    CPoint point = list.GetNext (pos);
    TRACE (_T ("x=%d, y=%d/n"), point.x, point.y);
}

 


As with CArray, the first template parameter specifies the data type (CPoint objects) and the second specifies how items are passed in parameter lists (by reference).

If you use classes rather than primitive data types in a CList and you call the list's Find function, your code won't compile unless one of the following conditions is true:


The class has an overloaded == operator that performs a comparison to a like object.


You override the template function CompareElements with a type-specific version that compares two instances of the class.

The first method—overloading the == operator—is the more common of the two and has already been done for you in MFC classes such as CPoint and CString. If you write a class yourself, you must do the operator overloading. Here's a modified version of CPoint3D that overloads the comparison operator for compatibility with CList::Find:

class CPoint3D
{
public:
    CPoint3D ()
    {
        x = y = z = 0;
    }
    CPoint3D (int xPos, int yPos, int zPos)
    {
        x = xPos;
        y = yPos;
        z = zPos;
    }
    operator== (CPoint3D point) const
    {
        return (x == point.x && y == point.y && z == point.z);
    }
    int x, y, z;
};

 


The alternative to overloading the comparison operator is to override the global CompareElements function, as demonstrated here:

class CPoint3D
{
public:
    CPoint3D ()
    {
        x = y = z = 0;
    }
    CPoint3D (int xPos, int yPos, int zPos)
    {
        x = xPos;
        y = yPos;
        z = zPos;
    }
    // Note: No operator==
    int x, y, z;
};

BOOL AFXAPI CompareElements (const CPoint3D* p1, const CPoint3D* p2)
{
    return (p1->x == p2->x && p1->y == p2->y && p1->z == p2->z);
}

 


Overriding CompareElements eliminates the need for operator overloading because the default implementation of CompareElements, which is called by CList::Find, compares items using the comparison operator. If you override CompareElements and don't use == in the override, you don't need to overload the == operator either.



//--------------------------------------------------------------------------------------------------------------------------------------------------//

关于MFC和STL的使用
赵湘宁
 
问题:
    我在几个不同的地方读过有关STL的内容,但还是不知道在我的应用程序中到底是用STL还是用MFC?我的程序也处理string,vectors等东西。使用STL和MFC到底有什么不同?
解答:
    要回答“我应该使用哪一个?”这样的问题,答案几乎都一样。它依赖于你想做什么,你的应用程序的种类以及你的知识。还有你的C++资格认证也很重要。当你需要快速做某件事情时,最好是利用你最熟悉的工具。如果你想要处理文本,而你有熟悉MFC的话,CString为首选。如果你熟悉STL,那就用string取代MFC。在这个世界上,你选择哪一个并不重要。使用string,list,或者大把的类都一样可以做好。但是有时要说这个或者哪个系统更好需要时间。例如,最近的一篇文章中有个例子程序RECYCLE,其中我就是用了STL,为什么呢?
    当我开始写这个程序时,我将它写成一个MFC的控制台应用——只因为我已经由一个写好的模板控制台应用。但是重新看了代码之后(你肯定也常常遇到这种情况)。我使用的都是MFC的CString和CstringList。因为程序要解析命令行参数,RECYCLE要建立一个删除文件的名字列表,文件名字都是CString类型的。如果仅仅为了处理串和列表而链接MFC的话似乎有些浪费资源。CStringList需要引入整个afxcoll.obj,CString需要加载strcore.obj,并且AfxWinInit肯定要初始化需要的模块。如果你不看映射文件,你绝对想象不到MFC要加载些什么东西。其实根本不用看,我就知道RECYCLE应该使用STL效率更高。
    为了将程序转换到STL并删除所有MFC的跟踪代码,我只要改为数不多的几行代码就可以了。首先,我#include<string>和<list>,然后为方便起见,添加一个类型定义:
// 串列表
typedef list<string> CStringList;

只有名字与MFC相同,而接口已经完全变了。尤其是STL使用iterators代替了POSITIONs。
CStringList files; // 文件名字列表

CStringList::iterator i;
for (i=files.begin(); i!=files.end(); i++) {

}

    比较之下,我发现STL的iterators比MFC的POSITIONs更容易记忆。不知什么原因,我总是记不住POSITIONs的使用方法,每次总是要去查手册,而begin/end 和 i++语法对我来说轻车熟路。另一方面,我希望STL有一个转换函数用于将string转成LPCTSTR,就像CString那样:
CString s; // MFC
LPCTSTR pstr = s; // 调用 "CString::operator LPCTSTR() const;"
MFC的转换函数很棒,使用它可以将某个CString传到任何可以传指针的的C串中。可以让你写如下的代码:
CString s = "whatever";
MyFunc(s); // MyFunc 想要 LPCTSTR
而如果使用STL,你必须显式地调用string::c_str.
string s;
MyFunc(s.c_str());
    也许STL的设计者认为转换函数容易引起混乱,确实是这样,正是因为如此才造就了整个STL设计。每次只要已涉及到STL代码就犯难,主要的问题是类型。使用STL的一个更重要的理由当然是可移植性。STL是C++标准的一部分,就像printf,,tolower,strcpy等是C标准的一部分一样。我总是感觉到可移植性有一些问题,因为大多数程序它依赖于平台。其它操作系统有
SHFileOperation吗?没有。然而,尽可能将平台依赖程度降至最小,STL能帮你做到。任何想要与ANSI兼容的编译器必须支持STL。但这种支持并不总是充分和成功。尤其是微软将STL看成一种负担,而不是把它当成一种好技术来对待。这显然是非常不明智的。
    另一方面,公正地讲,STL确实很神秘,表现在其内容划分、发生器以及容器等方面。并且它的模板代码总是让人不可接近(有点像ATL),阅读STL代码对所有人都是个严重的挑战。但Unix程序员可能体会到了STL所包含的东西,如果你熟悉了STL的那些单调乏味的术语,意想不到的函数名以及高度概括的代码,如果你能驾驭它们,那种感觉好极了。不久你就会融入到其中并发现STL的功能是多么强大,并且易于使用。STL遵循Unix的系统惯例,象SED,AWK和Emacs——难于学习,但功能强大并易于使用(我坦白承认打死都不会用Emacs)。如
果你真想做一些低级编程,那就认识STL吧!哪里能找到更多的有关STL的内容呢?网络上有很多关于STL的站点。只要用“Standard Template Library”或“STL”来搜索就可以找到一大把。最有名的一个站点是:
http://www.sgi.com/Technology/STL/index.html
你可以从上面找到容易理解文档和FAQ页面。

最后祝你编程愉快!


 

你可能感兴趣的:(String)