本文转载自:
POSITION用法解释
VC6.0 MFC中:POSITION用法解释
MFC中POSITION究竟是一个什么类型,CStringList类解释
关于MFC的POSITION
来源:POSITION用法解析
POSITION是MFC模板类库中经常使用的一个数据类型,我们从它的定义可以看出,其实,它就是一个指针。
// abstract iteration position
struct __POSITION { };
typedef __POSITION* POSITION;
MFC给出的注释是:一个抽象的迭代位置,既然是抽象,也就是没有给定具体的数据类型,以满足不同的模板参数的需要。
在CList中,经常会用到POSITION作为引用参数,或者作为返回值。下面,我们就举2个简单的例子,来帮助大家理解这个数据类型,不过在此之前,我要啰嗦一下,因为有很多人对__POSITION这个空结构体,表示怀疑,似乎这成为了他们理解的障碍。既然是一个空的结构体,怎么能用它来定义指针呢?
其实,C++编译器,不会将一个空的结构体完全看成是空的,编译器会为它分配1byte的内存,其实说白了__POSITION这个结构体,就相当于一个unsigned char的类型,所以__POSITION也就相当于是unsigned char,也就是Windows里面的BYTE类型,从而POSITION也就有了一个可以等价的形式,那就是BYTE*。
我们举CList中2个典型的成员函数作为讲解的重点,当然,大家应该知道CList是一个链表数据结构。下面看这两个函数:
1. POSITION GetHeadPosition() const;
显而易见,这个函数,是用来获取链表头的,它的返回值是一个POSITION,其实也就是一个指针。这个指针代表了谁?当然,就是你一直想要的链表头在链表中的指针,这个指针是new出来的,你绝对不能擅自的将这个指针delete掉。
2. TYPE& GetAt(POSITION position);
这个函数,看起来,比较怪。它的唯一的入参是一个指针,其实,有可能就是你刚刚用GetHeadPosition获得的头节点指针。这个函数的意思,也就是,需要你提供一个节点的地址,然后,CList会遍历整条链,去寻找并返回符合这个地址的节点数据。
废话了那么多,一言以蔽之,毕其功于一役,换句话说就是:
POSITION,你可以把它看成是一个学号,有了它,你就可以找到任何一个班里的学生。
POSITION在MFC里用来存放各种List或Array对象的索引,便于在遍历时标识元素的位置。其实就是一个32位值,其内容可能是一个指针,或是数组的Index。
来源:VC6.0 MFC中:POSITION用法解释
VC的基本头文件AFX.h里对POSITION是这样定义的:
struct __POSITION { };
typedef __POSITION* POSITION
显然这里定义了一个没有任何字段的结构体,而POSITION是这个结构体的指针。
VC在使用CList时,一些函数往往用POSITION参数,或者返回POSITION类型,其实查看CList的原代码就知道,原来CList返回的POSITION就是它的链表的一个结点指针,也就是说,在CList里有一个CNode的类定义了结点,而POSITION就是CNode*
所以说,虽然__POSITION结构体没有任何字段,但用POSITION结构体指针,仅仅用来表达CNode*指针,并且是强制转换过来的。于是POSITION被VC用为一种常见的数据类型---32位指针。
下面是CList中它的用法
AFX_INLINE TYPE& CList <TYPE, ARG_TYPE> ::GetNext(POSITION& rPosition) //
{
CNode* pNode = (CNode*) rPosition;
ASSERT(AfxIsValidAddress(pNode, sizeof(CNode)));
rPosition = (POSITION) pNode-> pNext;
return pNode-> data;
}
试了试空结构,很奇怪
struct SS{};
SS s={0}; 会分配4字节内存做s变量,但并不初始化这4字节为0
int b=sizeof(SS); 为1
在MFC4.0以前的版本中,POSITION实际上就是一个void指针。在5.0中的定义大概是:
struct __POSITION { int unused; } ;
typedef __POSITION * POSITION ;
6.0中才变为:
struct __POSITION { } ;
typedef __POSITION * POSITION ;
虽然结构内没有成员变量,但指向结构的指针仍需要4字节----32位机中天下的指针都是这个长度滴!
这样做的目的仅是为了类型安全。对于POSITION参数,使用者很容易传错,特别是当表中元素是void *时,此时很有可能在需要元素的地方传入POSITION,而在需要POSITION的地方传入void *。当做上面的定义中,编译器就可以检查出这种错误。
在MFC内部,POSITION实际上指向CNode类型。
来源:MFC中POSITION究竟是一个什么类型:CStringList类解释
下面是MFC的源代码,从CSDN的帖子中找到的,很好的共同学习、共同进步的门户啊。谢谢很多无私的人们!!
// abstract iteration position
struct __POSITION { };
typedef __POSITION* POSITION;
CStringList的声明:
...
protected:
struct CNode
{
CNode* pNext;
CNode* pPrev;
CString data;
};
CStringList的GetAt实现:
_AFXCOLL_INLINE CString& CStringList::GetAt(POSITION position)
{ CNode* pNode = (CNode*) position;
ASSERT(AfxIsValidAddress(pNode, sizeof(CNode)));
return pNode->data; }
所以POSITION实际就是一个指针,必要时MFC会把它转型为指向node的指针。
_AFXCOLL_INLINE CString& CStringList::GetNext(POSITION& rPosition)
{ CNode* pNode = (CNode*) rPosition;
ASSERT(AfxIsValidAddress(pNode, sizeof(CNode)));
rPosition = (POSITION) pNode->pNext;
return pNode->data; }
下面这个例子几乎用到cstringlist类所有的函数
CStringList var(15);
POSITION position;
position = var.InsertAfter(NULL,"item1");
position = var.InsertAfter(position,"item3");
position = var.InsertBefore(position,"item2");
for( position = var.GetHeadPosition(); NULL != position ; )
AfxMessageBox(var.GetNext(position));
for( position = var.GetTailPosition(); NULL != position ;var.GetPrev(position))
{
CString str;
str = var.GetAt(position);
if("item3"==str)
{
var.RemoveAt(position);
}
else
{
str.MakeUpper();
var.SetAt(position,str);
}
}
var.RemoveHead();
var.RemoveTail();
var.RemoveAll();
var.AddHead("he");
var.AddTail("dan");
position = var.Find("he");
var.SetAt(position,"He");
position = var.FindIndex(1);
var.SetAt(position,"Dan");
//end
下面是该类函数的简介
POSITION InsertBefore( POSITION position, CObject* newElement );
POSITION InsertAfter( POSITION position, CObject* newElement );
在一个位置前或后插入一个新元素.
POSITION GetHeadPosition( ) const;
POSITION GetTailPosition( ) const;
获得头位置和尾位置.
CObject*& GetNext( POSITION& rPosition );
CObject* GetNext( POSITION& rPosition ) const;
CObject*& GetPrev( POSITION& rPosition );
CObject* GetPrev( POSITION& rPosition ) const;
获得后一元素或前一元素,注意rPosition会变.
CObject*& GetAt( POSITION position );
CObject* GetAt( POSITION position ) const;
根据位置得到元素.
void SetAt( POSITION pos, CObject* newElement );
根据位置设置元素.
void RemoveAt( POSITION position );
根据位置删除元素.
CObject* RemoveHead( );删除并返回头元素
CObject* RemoveTail( ); 删除并返回尾元素
POSITION AddHead( CObject* newElement );增加头元素
void AddHead( CObList* pNewList );在前面加一个表
POSITION AddTail( CObject* newElement );增加尾元素
void AddTail( CObList* pNewList );在后面加一个表
POSITION Find( CObject* searchValue, POSITION startAfter = NULL ) const根据元素值找位置.
POSITION FindIndex( int nIndex ) const根据索引找位置.
来源:关于MFC的POSITION
struct __POSITION {};
typedef __POSITION* POSITION;
从这两句代码可以看出POSITION实质是一个空结构体的指针。
疑问一:为什么不用迭代器做指针?
STL相对于MFC来说是晚辈,所以不要指望MFC的工程师会继承迭代器的一切思想。
疑问二:为什么不用void* ?
这样做的目的是为了类型安全。对于POSITION参数,使用者很容易传错,特别是当表中元素是void *时,此时很有可能在需要元素的地方传入POSITION,而在需要POSITION的地方传入void *。当做上面的定义中,编译器就可以检查出这种错误。
事实上在MFC4.0之前,POSITION是一个void指针。
在MFC5.0中,POSITION是
struct __POSITION { int unused; };
typedef __POSITION* POSITION;