MFC 的CList,CPtrList,CObList,CStringList 的用法
一直在編程中使用stl進(jìn)行編程,但是最近感覺微軟提供的模版類也是很好,出錯(cuò)機(jī)率更低,建議在mfc的工程使用。相關(guān)介紹如下,參照msdn搞清接口即可使用
CList是通用型的列表類,你可以存放指定的數(shù)據(jù)類型,用法如下:
CList<CPoint, CPoint&> list;
這樣就指定了CList中存放的是CPoint類型的引用;
CPtrList,CObList,CStringList都是具體的用于某種類型的集合類型
而CPtrList存放的是void類型的指針,取出的時(shí)候必須要強(qiáng)制轉(zhuǎn)換;
CObList用于存放從CObject派生的數(shù)據(jù)類型的指針;
CStringList存放CString字符串類型,相當(dāng)于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.
//--------------------------------------------------------------------------------------------------------------------------------------------------//
| 關(guān)于MFC和STL的使用 |
| 趙湘寧 |
| ? |
| 問題: |
| ??? 我在幾個(gè)不同的地方讀過有關(guān)STL的內(nèi)容,但還是不知道在我的應(yīng)用程序中到底是用STL還是用MFC?我的程序也處理string,vectors等東西。使用STL和MFC到底有什么不同? 解答: ??? 要回答“我應(yīng)該使用哪一個(gè)?”這樣的問題,答案幾乎都一樣。它依賴于你想做什么,你的應(yīng)用程序的種類以及你的知識(shí)。還有你的C++資格認(rèn)證也很重要。當(dāng)你需要快速做某件事情時(shí),最好是利用你最熟悉的工具。如果你想要處理文本,而你有熟悉MFC的話,CString為首選。如果你熟悉STL,那就用string取代MFC。在這個(gè)世界上,你選擇哪一個(gè)并不重要。使用string,list,或者大把的類都一樣可以做好。但是有時(shí)要說這個(gè)或者哪個(gè)系統(tǒng)更好需要時(shí)間。例如,最近的一篇文章中有個(gè)例子程序RECYCLE,其中我就是用了STL,為什么呢? ??? 當(dāng)我開始寫這個(gè)程序時(shí),我將它寫成一個(gè)MFC的控制臺(tái)應(yīng)用——只因?yàn)槲乙呀?jīng)由一個(gè)寫好的模板控制臺(tái)應(yīng)用。但是重新看了代碼之后(你肯定也常常遇到這種情況)。我使用的都是MFC的CString和CstringList。因?yàn)槌绦蛞馕雒钚袇?shù),RECYCLE要建立一個(gè)刪除文件的名字列表,文件名字都是CString類型的。如果僅僅為了處理串和列表而鏈接MFC的話似乎有些浪費(fèi)資源。CStringList需要引入整個(gè)afxcoll.obj,CString需要加載strcore.obj,并且AfxWinInit肯定要初始化需要的模塊。如果你不看映射文件,你絕對想象不到MFC要加載些什么東西。其實(shí)根本不用看,我就知道RECYCLE應(yīng)該使用STL效率更高。 ??? 為了將程序轉(zhuǎn)換到STL并刪除所有MFC的跟蹤代碼,我只要改為數(shù)不多的幾行代碼就可以了。首先,我#include<string>和<list>,然后為方便起見,添加一個(gè)類型定義: // 串列表 typedef list<string> CStringList; 只有名字與MFC相同,而接口已經(jīng)完全變了。尤其是STL使用iterators代替了POSITIONs。 CStringList files; // 文件名字列表 … CStringList::iterator i; for (i=files.begin(); i!=files.end(); i++) { … } ??? 比較之下,我發(fā)現(xiàn)STL的iterators比MFC的POSITIONs更容易記憶。不知什么原因,我總是記不住POSITIONs的使用方法,每次總是要去查手冊,而begin/end 和 i++語法對我來說輕車熟路。另一方面,我希望STL有一個(gè)轉(zhuǎn)換函數(shù)用于將string轉(zhuǎn)成LPCTSTR,就像CString那樣: CString s; // MFC LPCTSTR pstr = s; // 調(diào)用 "CString::operator LPCTSTR() const;" MFC的轉(zhuǎn)換函數(shù)很棒,使用它可以將某個(gè)CString傳到任何可以傳指針的的C串中??梢宰屇銓懭缦碌拇a: CString s = "whatever"; MyFunc(s); // MyFunc 想要 LPCTSTR 而如果使用STL,你必須顯式地調(diào)用string::c_str. string s; MyFunc(s.c_str()); ??? 也許STL的設(shè)計(jì)者認(rèn)為轉(zhuǎn)換函數(shù)容易引起混亂,確實(shí)是這樣,正是因?yàn)槿绱瞬旁炀土苏麄€(gè)STL設(shè)計(jì)。每次只要已涉及到STL代碼就犯難,主要的問題是類型。使用STL的一個(gè)更重要的理由當(dāng)然是可移植性。STL是C++標(biāo)準(zhǔn)的一部分,就像printf,,tolower,strcpy等是C標(biāo)準(zhǔn)的一部分一樣。我總是感覺到可移植性有一些問題,因?yàn)榇蠖鄶?shù)程序它依賴于平臺(tái)。其它操作系統(tǒng)有 SHFileOperation嗎?沒有。然而,盡可能將平臺(tái)依賴程度降至最小,STL能幫你做到。任何想要與ANSI兼容的編譯器必須支持STL。但這種支持并不總是充分和成功。尤其是微軟將STL看成一種負(fù)擔(dān),而不是把它當(dāng)成一種好技術(shù)來對待。這顯然是非常不明智的。 ??? 另一方面,公正地講,STL確實(shí)很神秘,表現(xiàn)在其內(nèi)容劃分、發(fā)生器以及容器等方面。并且它的模板代碼總是讓人不可接近(有點(diǎn)像ATL),閱讀STL代碼對所有人都是個(gè)嚴(yán)重的挑戰(zhàn)。但Unix程序員可能體會(huì)到了STL所包含的東西,如果你熟悉了STL的那些單調(diào)乏味的術(shù)語,意想不到的函數(shù)名以及高度概括的代碼,如果你能駕馭它們,那種感覺好極了。不久你就會(huì)融入到其中并發(fā)現(xiàn)STL的功能是多么強(qiáng)大,并且易于使用。STL遵循Unix的系統(tǒng)慣例,象SED,AWK和Emacs——難于學(xué)習(xí),但功能強(qiáng)大并易于使用(我坦白承認(rèn)打死都不會(huì)用Emacs)。如 果你真想做一些低級(jí)編程,那就認(rèn)識(shí)STL吧!哪里能找到更多的有關(guān)STL的內(nèi)容呢?網(wǎng)絡(luò)上有很多關(guān)于STL的站點(diǎn)。只要用“Standard Template Library”或“STL”來搜索就可以找到一大把。最有名的一個(gè)站點(diǎn)是: http://www.sgi.com/Technology/STL/index.html 你可以從上面找到容易理解文檔和FAQ頁面。 最后祝你編程愉快! |
?
總結(jié)
以上是生活随笔為你收集整理的MFC 的CList,CPtrList,CObList,CStringList 的用法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: KEPServerEX 6 之 lot
- 下一篇: 第一个爬虫