如何:创建类型安全集合

本文介绍如何针对您自己的数据类型生成类型安全的集合。 主题包括:

Microsoft 基础类库提供基于 C++ 模板的预定义类型安全集合。 由于它们是模板,因此这些类帮助提供类型安全并且易于使用,不必进行类型转换和其他与为了此目的使用非模板类有关的额外的工作。 MFC 示例 COLLECT 演示了在 MFC 应用程序中使用基于模板的集合类的情况。 一般将在编写新集合代码时使用这些类。

为类型安全性使用基于模板的类

使用基于模板的类

  1. 声明集合类类型的变量。 例如:

    CList<int, int> m_intList;
    
  2. 调用集合对象的成员函数。 例如:

    m_intList.AddTail(100);
    m_intList.RemoveAll();
    
  3. 如有必要,请实现帮助程序帮助程序SerializeElements。 有关实现这些函数的信息,请参阅实现帮助程序函数

此示例演示对整数列表的声明。 步骤 1 中的第一个参数是作为列表的元素存储的数据的类型。 第二个参数指定如何将数据传递到集合类的成员函数(如 AddGetAt)以及如何从这些函数返回数据。

实现帮助程序函数

基于模板的集合类 CArrayCListCMap 使用您可根据需要为派生集合类自定义的 5 个全局帮助器函数。 有关这些帮助程序函数的信息,请参阅 MFC 参考中的集合类帮助程序。 基于模板的集合类的大部分使用需要实现序列化函数。

序列化元素

CArrayCListCMap 类将调用 SerializeElements 以将集合元素存储到存档中或从存档中读取集合元素。

SerializeElements 帮助器函数的默认实现将执行从对象到存档的位写入,或从存档到对象的位读取,具体取决于是将对象存储在存档中还是从存档中检索对象。 如果此操作不合适,则请重写 SerializeElements

如果您的集合存储派生自 CObject 的对象并且您在集合元素类的实现中使用 IMPLEMENT_SERIAL 宏,则可以使用内置于 CArchiveCObject 的序列化功能:

CArray< CPerson, CPerson& > personArray;

template <> void AFXAPI SerializeElements <CPerson>(CArchive& ar,
   CPerson* pNewPersons, INT_PTR nCount)
{
   for (int i = 0; i < nCount; i++, pNewPersons++)
   {
      // Serialize each CPerson object
      pNewPersons->Serialize(ar);
   }
}

CArchive 的重载插入运算符为每个 CPerson 对象调用 CObject::Serialize(或该函数的替代项)。

使用非模板集合类

MFC 还支持 MFC 1.0 版引入的集合类。 这些类都不基于模板。 它们可用于包含支持的类型 CObject*UINTDWORDCString 的数据。 您可以使用这些预定义的集合(如 CObList)保存派生自 CObject 的任何对象的集合。 MFC 还提供其他预定义集合以保存基元类型(如 UINT)和 void 指针 (void*)。 但一般来说,定义自己的类型安全集合来保存更特定的类的对象及其派生类型往往很有用。 请注意,使用不基于模板的集合类这样做比使用基于模板的类工作量大。

使用非模板集合创建类型安全的集合有两种方式:

  1. 如有必要,将非模板集合与类型转换一起使用。 这是更简单的方法。

  2. 从非模板类型安全集合派生和扩展非模板类型安全集合。

将非模板集合与类型转换一起使用

  1. 直接使用非模板类之一(如 CWordArray)。

    例如,您可以创建一个 CWordArray 并为其添加任何 32 位值,然后检索这些值。 再无任何操作。 只需使用预定义函数。

    您也可以使用预定义集合(如 CObList)保存派生自 CObject 的任何对象。 将定义 CObList 集合以保存指向 CObject 的指针。 从列表中检索对象时,您可能必须将结果转换为适当的类型,因为 CObList 函数将返回指向 CObject 的指针。 例如,如果您将 CPerson 对象存储在 CObList 集合中,则必须将检索的元素转换为指向 CPerson 对象的指针。 以下示例使用 CObList 集合保存 CPerson 对象:

    CPerson* p1 = new CPerson();
    CObList myList;
    
    myList.AddHead(p1);   // No cast needed
    CPerson* p2 = (CPerson*)myList.GetHead();
    

    使用预定义集合类型和根据需要进行转换的方法可能满足您集合的许多需求。 如果您需要更多函数或更多类型安全,请使用基于模板的类或执行下面的过程。

派生和扩展非模板类型安全的集合

  1. 从预定义的非模板类派生您自己的集合类。

    派生类时,您可以添加类型安全的包装器函数以为现有函数提供一个类型安全的接口。

    例如,如果要从 CObList 派生一个列表来保存 CPerson 对象,则您可能添加包装器函数 AddHeadPersonGetHeadPerson,如下所示。

    class CPersonList : public CObList
    {
    public:
       void AddHeadPerson(CPerson* person)
       {
          AddHead(person);
       }
    
       const CPerson* GetHeadPerson()
       {
          return (CPerson*)GetHead();
       }
    };
    

    这些包装器函数为从派生列表添加和检索 CPerson 对象提供了一种类型安全的方法。 您可以看到,对于 GetHeadPerson 函数,您将封装类型转换。

    您还可通过定义扩展集合功能的新函数而不是将现有函数包装在类型安全的包装器中来添加新函数。 例如,删除 CObject 集合中的所有对象一文介绍了可用于删除列表中包含的所有对象的函数。 此函数无法作为成员函数添加到派生类中。

另请参阅

集合