看桌子 http://msdn.microsoft.com/en-us/library/y1z022s1%28v=vs.80%29.aspx#_core_collection_shape_features
我无法看到MFC集合用于我需要的目的。
http://msdn.microsoft.com/en-us/library/s897094z%28v=vs.80%29.aspx上的CMap文档也说明了
“你可能认为这个迭代是按键值顺序的;它不是。检索到的元素序列是不确定的。”
正如我所期望的那样,我认为它使用哈希算法。
我需要的是一本具有以下功能的字典:
我想知道有多少次我可以在真实应用中应用这种模式。 您可以为此建议我的最佳解决方案是什么?
注意:前一段时间我在C#中也遇到了同样的问题。它的解决方案也是受欢迎的。如果我没记错,SortedDictionary按键排序,因此不适合。
编辑:即使最好 - 只是为了不与现有的代码库不协调 - 使用MFC中的东西,它也不是义务。如果它是标准的C ++,你可以建议你想要的任何东西。
EDIT2:为了提高清晰度:容器的每个元素的描述都是
{
int Unique NonNullable OrderIndex,
enum KeyEnum Unique NonNullable key,
enum ValueEnum NotUnique NonNullable value
}
如果将它实现为类似动态数组的东西,我甚至不关心存储OrderIndex。对于这个,我真的只需要一个唯一的值来表示相对位置,并且有可能交换位置元素。
提前致谢,
塞尔吉奥
答案 0 :(得分:1)
MFC不提供满足您需求的容器,但您可以使用std::map
或std::set
代替。每个都会保持钥匙的独特性;您选择的将取决于您是否希望将密钥与有效负载分开或集成到单个对象中。
答案 1 :(得分:0)
当你说'Ordered'时,你已经间接暗示'order index'是唯一的'key'。你为什么不能使用std :: vector?
如果你想拥有另一个唯一的密钥,你可以使用std :: map,但你必须将'order'作为密钥/值组合的一部分进行即兴创作。我看到最终的解决方案将是复合的。你将拥有自己的类来支持你想要的东西,其中tern可以std :: map实现这个或任何其他包含类似列表。
答案 2 :(得分:0)
在C ++ Freenode的IRC频道上进行这次对话之后,我觉得我很想尝试Boost Multi索引(zxvf就是我):
zxvf hello
zxvf I was trying to solve the problem of this question: http://stackoverflow.com/questions/9329011/mfc-dictionary-collection-with-key-unicity-and-ordering-by-position/9438561#9438561
zxvf what kind of container would you recommend to me?
TinoDidriksen std::vector sounds fitting.
zxvf you can not guarantee unicity of keyEnum, TinoDidriksen
MrBar zxvf: so use std::map then
zxvf MrBar: I think you all didn't get the point of the problem
evilissimo I think you'd need something like boost multi_index
zxvf I want both unicity of KeyEnum and OrderIndex
MrBar zxvf: i get point, but that container u asked about not exist
TinoDidriksen zxvf, Boost.Multi_index then.
evilissimo zxvf, by unicity you mean that there are at no given time two of any of them in the container?
evilissimo or by the combination?
MrBar zxvf: u need to use intermix of containers
zxvf and NO, I don't want unicity of the pair <OrderIndex, KeyEnum> but unicity of each one
evilissimo zxvf, had to ask
zxvf seeing what the heck is Boost Multi index
MrBar zxvf: noooo, not see in boost there no back way
zxvf MrBar, could you rephrase please?
zxvf Not english native speaker, me
evilissimo zxvf, that wasn't even valid english
MrBar zxvf: hmm i too
evilissimo zxvf, basically there are tons of useful stuff in boost, and usually once you start loving one library you start using more of them
zxvf thanks, people. It's aready late here and I will not see this tonight. But seems to me that Boost Multi index is a very good help for me
答案 3 :(得分:0)
使用数组并将索引存储在数组中作为值的元素。如果需要,使用另一个CMap进行快速哈希访问。我知道并听到的地图都不支持订单索引号。
答案 4 :(得分:0)
我决定从CMap和CArray派生一个模板化的类,并按如下方式编写ArrayMapTempl.h文件:
//Author: Sérgio Loureiro. This is open source.
#include <afxtempl.h>
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
class CArrayMap: protected CArray<KEY,ARG_KEY>, protected CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>
{
private:
bool m_isChangingSize;
public:
CArrayMap():
m_isChangingSize(false)
{
};
CArrayMap(const CArrayMap& src){};
CArrayMap operator=(const CArrayMap& src){return *this;};
// Attributes
INT_PTR GetSize() const;
INT_PTR GetCount() const;
BOOL IsEmpty() const;
INT_PTR GetUpperBound() const;
// Lookup
int Lookup(ARG_KEY key) const;
int Lookup(ARG_KEY key, VALUE& rValue) const;
// Operations
// Clean up
void RemoveAll();
// Accessing elements
const CPair& GetAt(INT_PTR nIndex) const;
CPair& GetAt(INT_PTR nIndex);
void SetAt(INT_PTR nIndex, ARG_KEY newKey, ARG_VALUE newValue);
const CPair& ElementAt(INT_PTR nIndex) const;
CPair& ElementAt(INT_PTR nIndex);
// Direct Access to the element data
const CPair& GetData() const;
CPair& GetData();
// Potentially growing the array
INT_PTR Add(ARG_KEY newKey, ARG_VALUE newValue);
void Copy(const CArrayMap& src);
// overloaded operator helpers
const CPair& operator[](INT_PTR nIndex) const;
CPair& operator[](INT_PTR nIndex);
// Operations that move elements around
BOOL Swap(INT_PTR nIndex, INT_PTR nIndex2);
BOOL MoveUp(INT_PTR nIndex);
BOOL MoveDown(INT_PTR nIndex);
BOOL SwapByKey(ARG_KEY key, ARG_KEY key2);
BOOL MoveUpByKey(ARG_KEY key);
BOOL MoveDownByKey(ARG_KEY key);
BOOL InsertAt(INT_PTR nIndex, ARG_KEY newKey, ARG_VALUE newValue);
BOOL RemoveAt(INT_PTR nIndex);
BOOL RemoveByKey(ARG_KEY key);
public:
void Serialize(CArchive&);
#ifdef _DEBUG
void Dump(CDumpContext&) const;
void AssertValid() const;
#endif
#if 0
public:
// Construction
CArray();
// Attributes
INT_PTR GetSize() const;
INT_PTR GetCount() const;
BOOL IsEmpty() const;
INT_PTR GetUpperBound() const;
void SetSize(INT_PTR nNewSize, INT_PTR nGrowBy = -1);
// Operations
// Clean up
void FreeExtra();
void RemoveAll();
// Accessing elements
const TYPE& GetAt(INT_PTR nIndex) const;
TYPE& GetAt(INT_PTR nIndex);
void SetAt(INT_PTR nIndex, ARG_TYPE newElement);
const TYPE& ElementAt(INT_PTR nIndex) const;
TYPE& ElementAt(INT_PTR nIndex);
// Direct Access to the element data (may return NULL)
const TYPE* GetData() const;
TYPE* GetData();
// Potentially growing the array
void SetAtGrow(INT_PTR nIndex, ARG_TYPE newElement);
INT_PTR Add(ARG_TYPE newElement);
INT_PTR Append(const CArray& src);
void Copy(const CArray& src);
// overloaded operator helpers
const TYPE& operator[](INT_PTR nIndex) const;
TYPE& operator[](INT_PTR nIndex);
// Operations that move elements around
void RemoveAt(INT_PTR nIndex, INT_PTR nCount = 1);
void InsertAt(INT_PTR nStartIndex, CArray* pNewArray);
// Implementation
protected:
TYPE* m_pData; // the actual array of data
INT_PTR m_nSize; // # of elements (upperBound - 1)
INT_PTR m_nMaxSize; // max allocated
INT_PTR m_nGrowBy; // grow amount
public:
~CArray();
void Serialize(CArchive&);
#ifdef _DEBUG
void Dump(CDumpContext&) const;
void AssertValid() const;
#endif
#endif
//----------------------------------------------------------------------------------------
#if 0
public:
// CPair
struct CPair
{
const KEY key;
VALUE value;
protected:
CPair( ARG_KEY keyval ) : key( keyval ) {}
};
protected:
// Association
class CAssoc : public CPair
{
friend class CMap<KEY,ARG_KEY,VALUE,ARG_VALUE>;
CAssoc* pNext;
UINT nHashValue; // needed for efficient iteration
public:
CAssoc( ARG_KEY key ) : CPair( key ) {}
};
public:
// Construction
/* explicit */ CMap(INT_PTR nBlockSize = 10);
// Attributes
// number of elements
INT_PTR GetCount() const;
INT_PTR GetSize() const;
BOOL IsEmpty() const;
// Lookup
BOOL Lookup(ARG_KEY key, VALUE& rValue) const;
const CPair *PLookup(ARG_KEY key) const;
CPair *PLookup(ARG_KEY key);
// Operations
// Lookup and add if not there
VALUE& operator[](ARG_KEY key);
// add a new (key, value) pair
void SetAt(ARG_KEY key, ARG_VALUE newValue);
// removing existing (key, ?) pair
BOOL RemoveKey(ARG_KEY key);
void RemoveAll();
// iterating all (key, value) pairs
POSITION GetStartPosition() const;
const CPair *PGetFirstAssoc() const;
CPair *PGetFirstAssoc();
void GetNextAssoc(POSITION& rNextPosition, KEY& rKey, VALUE& rValue) const;
const CPair *PGetNextAssoc(const CPair *pAssocRet) const;
CPair *PGetNextAssoc(const CPair *pAssocRet);
// advanced features for derived classes
UINT GetHashTableSize() const;
void InitHashTable(UINT hashSize, BOOL bAllocNow = TRUE);
// Implementation
protected:
CAssoc** m_pHashTable;
UINT m_nHashTableSize;
INT_PTR m_nCount;
CAssoc* m_pFreeList;
struct CPlex* m_pBlocks;
INT_PTR m_nBlockSize;
CAssoc* NewAssoc(ARG_KEY key);
void FreeAssoc(CAssoc*);
CAssoc* GetAssocAt(ARG_KEY, UINT&, UINT&) const;
public:
~CMap();
void Serialize(CArchive&);
#ifdef _DEBUG
void Dump(CDumpContext&) const;
void AssertValid() const;
#endif
#endif
};
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
inline INT_PTR CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::GetSize() const
{
ASSERT(CArray::m_nSize == CMap::m_nCount);
return CArray::GetSize();
};
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
inline INT_PTR CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::GetCount() const
{
ASSERT(CArray::m_nSize == CMap::m_nCount);
return CArray::GetCount();
}
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
inline BOOL CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::IsEmpty() const
{
ASSERT(CArray::m_nSize == CMap::m_nCount);
return CArray::IsEmpty();
}
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
inline INT_PTR CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::GetUpperBound() const
{
ASSERT(CArray::m_nSize == CMap::m_nCount);
return CArray::GetUpperBound();
}
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
inline INT_PTR CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::Add(ARG_KEY newKey, ARG_VALUE newValue)
{
VALUE rValue;
if( CMap::Lookup(newKey,rValue)) //already exists
return -1;
INT_PTR nIndex = CArray::m_nSize; //old size will be the new position
m_isChangingSize= true;
CMap::operator[] (newKey)= newValue;
CArray::Add(newKey);
m_isChangingSize= false;
ASSERT(CArray::m_nSize == CMap::m_nCount);
return nIndex;
};
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
inline const typename CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::CPair&
CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::GetAt(INT_PTR nIndex) const
{
ASSERT(nIndex >= 0 && nIndex < m_nSize);
if(nIndex >= 0 && nIndex < m_nSize)
{
return *CMap::PLookup(CArray::GetAt(nIndex));
}
AfxThrowInvalidArgException();
};
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
inline typename CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::CPair&
CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::GetAt(INT_PTR nIndex)
{
ASSERT(nIndex >= 0 && nIndex < m_nSize);
if(nIndex >= 0 && nIndex < m_nSize)
{
return *CMap::PLookup(CArray::GetAt(nIndex));
}
AfxThrowInvalidArgException();
};
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
int CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::Lookup(ARG_KEY key) const
{
VALUE rValue;
return this->Lookup(key, rValue);
};
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
int CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::Lookup(ARG_KEY key, VALUE& rValue) const
{
for (int i=0; i<m_nSize ;i++)
{
if(CArray::operator [] ( i ) == key && CMap::Lookup(key, rValue))
{
return i;
}
}
return -1;
};
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
void CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::RemoveAll()
{
m_isChangingSize=true;
CMap::RemoveAll();
CArray::RemoveAll();
m_isChangingSize=false;
ASSERT(CArray::m_nSize == CMap::m_nCount);
};
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
BOOL CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::Swap(INT_PTR nIndex, INT_PTR nIndex2)
{
if(nIndex<0 || nIndex2<0)
return FALSE;
if(nIndex>=m_nSize || nIndex2>=m_nSize)
return FALSE;
//Swap with itself. Everything is fine and nothing needs to be done
if(nIndex == nIndex2)
return TRUE;
KEY k= CArray::GetAt(nIndex);
CArray::SetAt(nIndex, CArray::GetAt(nIndex2));
CArray::SetAt(nIndex, k);
};
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
BOOL CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::MoveUp(INT_PTR nIndex)
{
if (nIndex == 0)
return FALSE;
return Swap(nIndex,nIndex-1);
};
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
BOOL CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::MoveDown(INT_PTR nIndex)
{
if (nIndex == m_nSize-1)
return FALSE;
return Swap(nIndex,nIndex+1);
};
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
BOOL CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::SwapByKey(ARG_KEY key, ARG_KEY key2)
{
int nIndex= Lookup(key);
int nIndex2= Lookup(key2);
if(nIndex == -1 || nIndex2 == -1)
return FALSE;
return Swap(nIndex,nIndex2);
}
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
BOOL CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::MoveUpByKey(ARG_KEY key)
{
int nIndex= Lookup(key);
if(nIndex == -1)
return FALSE;
return MoveUp(nIndex);
}
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
BOOL CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::MoveDownByKey(ARG_KEY key)
{
int nIndex= Lookup(key);
if(nIndex == -1)
return FALSE;
return MoveDown(nIndex);
}
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
int CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::InsertAt(INT_PTR nIndex,ARG_KEY newKey, ARG_VALUE newValue)
{
AssertValid(); //ASSERT_VALID(this);
if(nIndex < 0)
return FALSE; //AfxThrowInvalidArgException();
if(nIndex > m_nSize) //doesn't make sense to grow more than last+1 , given newKey has to be unique
return FALSE;
//I am not using this->Lookup(ARG_KEY key), because I
//presume CMap::Lookup(ARG_KEY key, VALUE& rValue) will be faster,
//as it does not need to traverse the array.
VALUE rValue;
if(CMap::Lookup(newKey,rValue)) //already exists
return FALSE;
m_isChangingSize=true;
CMap::operator[] (newKey)= newValue;
CArray::InsertAt(nIndex,newKey,1);
m_isChangingSize=false;
ASSERT(CArray::m_nSize == CMap::m_nCount);
return TRUE;
}
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
BOOL CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::RemoveAt(INT_PTR nIndex)
{
if(nIndex<0 || nIndex>= m_nSize)
return FALSE;
KEY k= CArray::GetAt(nIndex);
//I am not using this->Lookup(ARG_KEY key), because I
//presume CMap::Lookup(ARG_KEY key, VALUE& rValue) will be faster,
//as it does not need to traverse the array.
VALUE rValue;
if(CMap::Lookup(k,rValue)) //already exists
{
m_isChangingSize= true;
CMap::RemoveKey(k);
CArray::RemoveAt(nIndex);
m_isChangingSize= false;
ASSERT(CArray::m_nSize == CMap::m_nCount);
return TRUE;
}
else
return FALSE;
};
template <class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
BOOL CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::RemoveByKey(ARG_KEY key)
{
int nIndex= Lookup(key);
if(nIndex == -1)
return FALSE;
KEY k= CArray::GetAt(nIndex);
return RemoveAt(nIndex);
};
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
void CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::Serialize(CArchive& ar)
{
ASSERT(CArray::m_nSize == CMap::m_nCount);
//ASSERT_VALID((const CArray *)this);
//ASSERT_VALID((const CMap *)this);
CObject::Serialize(ar);
if (ar.IsStoring())
{
ar.WriteCount(m_nSize);
if (m_nSize == 0)
return; // nothing more to do
for(INT_PTR i=0;i<m_nSize;i++)
{
KEY* pKey;
VALUE* pValue;
/*
* in some cases the & operator might be overloaded, and we cannot use it to
* obtain the address of a given object. We then use the following trick to
* get the address
*/
pKey = reinterpret_cast< KEY* >( &reinterpret_cast< int& >( const_cast< KEY& > ( static_cast< const KEY& >( CArray::operator[]( i ) ) ) ) );
pValue = reinterpret_cast< VALUE* >( &reinterpret_cast< int& >( static_cast< VALUE& >( CMap::operator[]( *pKey ) ) ) );
SerializeElements<KEY>(ar, pKey, 1);
SerializeElements<VALUE>(ar, pValue, 1);
}
}
else
{
DWORD_PTR nNewCount = ar.ReadCount();
while (nNewCount--)
{
KEY newKey[1];
VALUE newValue[1];
SerializeElements<KEY>(ar, newKey, 1);
SerializeElements<VALUE>(ar, newValue, 1);
this->Add(newKey[0], newValue[0]); //includes checking if it already exists
}
}
}
#ifdef _DEBUG
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
void CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::Dump(CDumpContext& dc) const
{
ASSERT(CArray::m_nSize == CMap::m_nCount);
CObject::Dump(dc);
dc << "with " << m_nSize << " elements";
if (dc.GetDepth() > 0)
{
// Dump in format "[key] -> value"
KEY key[1];
VALUE val[1];
POSITION pos = GetStartPosition();
while (pos != NULL)
for (int i=0; i<m_nSize; i++)
{
key[0]= CArray::operator[]( i );
//val[0]= CMap::operator[]( key[0] );
CMap::Lookup(key[0], val[0]);
dc << "\n\t[";
DumpElements<KEY>(dc, key, 1);
dc << "] = ";
DumpElements<VALUE>(dc, val, 1);
}
}
dc << "\n";
}
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
void CArrayMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::AssertValid() const
{
CArray::AssertValid();
CMap::AssertValid();
if(!m_isChangingSize)
ASSERT(CArray::m_nSize == CMap::m_nCount);
}
#endif //_DEBUG
现在我拥有了我需要的一切。我没有测试所有东西,但我相信如果需要的话,只需要很少的修正。
无论如何,谢谢大家的回答和评论。