网管联盟 | 网管论坛 | 网管u家 | 网管博客 | 网管软件 | 网管求职 | 小游戏 | 网管搜索 | 网管原创 | 网管聚合 | 网管读摘 | 网管焦点 | 世界素材 | 会员投稿 | 会员中心 
中国网管联盟
Windows Linux Cisco 网络技术 数据库 黑客攻防 DotNet Java PHP 认证 新闻资讯 服务器 存储资讯 网络设备 网管学堂 技术专题 焦点 网吧频道
 当前位置: > bitsCN.com > linux > Linux编程 > C++编程 > 在 C/C++ 中如何构造通用的对象链表  

在 C/C++ 中如何构造通用的对象链表

2004-10-11  作者:bitsCN整理  来源:IBM.COM  点评 投稿 收藏


  一个简化的问题示例
  链表的难点在于必须复制链表处理函数来处理不同的对象,即便逻辑是完全相同的。例如:
  
  两个结构类似的链表
  struct Struct_Object_A
  {
    int a;
    int b;
    Struct_Object_A *next;
  
  } OBJECT_A;
  
  typedef struct Struct_Object_B
  {
    int a;
    int b;
    int c;
    Struct_Object_B *next;
  
  } OBJECT_B;   
  上面定义的两个结构只有很小的一点差别。OBJECT_B 和 OBJECT_A 之间只差一个整型变量。但是,在编译器看来,它们仍然是非常不同的。必须为存储在链表中的每个对象复制用来添加、删除和搜索链表的函数。为了解决这个问题,可以使用具有全部三个变量的一个联合或结构,其中整数 c 并不是在所有的情况下都要使用。这可能变得非常复杂,并会形成不良的编程风格。
  
  C 代码解决方案:虚拟链表
  此问题更好的解决方案之一是虚拟链表。虚拟链表是只包含链表指针的链表。对象存储在链表结构背后。这一点是这样实现的,首先为链表节点分配内存,接着为对象分配内存,然后将这块内存分配给链表节点指针,如下所示:
网管网www.bitscn.com

  
  虚拟链表结构的一种实现
  typedef struct liststruct
  {
    liststruct *next;
  
  } LIST, *pLIST;
  
  pLIST Head = NULL;
  
  pLIST AddToList( pLIST Head, void * data, size_t datasize )
  {
  pLIST newlist=NULL;
  void *p;
  
    // 分配节点内存和数据内存
    newlist = (pLIST) malloc( datasize + sizeof( LIST ) );
  
    // 为这块数据缓冲区指定一个指针
    p = (void *)( newlist + 1 );
  
    // 复制数据
    memcpy( p, data, datasize );
  
    // 将这个节点指定给链表的表头
    if( Head )
    {
    newlist->next = Head;
    }
    else
    newlist->next = NULL;
  
    Head = newlist;
  
    return Head;
  }   
  链表节点现在建立在数据值副本的基本之上。这个版本能很好地处理标量值,但不能处理带有用 malloc 或 new 分配的元素的对象。要处理这些对象,LIST 结构需要包含一个一般的解除函数指针,这个指针可用来在将节点从链表中删除并解除它之前释放内存(或者关闭文件,或者调用关闭方法)。 网管联盟bitsCN_com
  
  一个带有解除函数的链表
  typedef void (*ListNodeDestructor)( void * );
  
  typedef struct liststruct
  {
    ListNodeDestructor DestructFunc;
    liststruct *next;
  
  } LIST, *pLIST;
  
  pLIST AddToList( pLIST Head, void * data, size_t datasize,
  ListNodeDestructor Destructor )
  {
  pLIST newlist=NULL;
  void *p;
  
  
    // 分配节点内存和数据内存
    newlist = (pLIST) malloc( datasize + sizeof( LIST ) );
  
    // 为这块数据缓冲区指定一个指针
    p = (void *)( newlist + 1 );
  
    // 复制数据
    memcpy( p, data, datasize );
  
    newlist->DestructFunc = Destructor;
    
    // 将这个节点指定给链表的表头
    if( Head )
    {
      newlist->next = Head;
    }
    else
      newlist->next = NULL;
  
    Head = newlist;
  
    return Head;
  }
   网管网www.bitscn.com
  void DeleteList( pLIST Head )
  {
    pLIST Next;
    while( Head )
    {
      Next = Head->next;
      Head->DestructFunc( (void *) Head );
      free( Head );
      Head = Next;
    }
  }
  
  typedef struct ListDataStruct
  {
    LPSTR p;
  
  } LIST_DATA, *pLIST_DATA;
  
  void ListDataDestructor( void *p )
  {
    // 对节点指针进行类型转换
    pLIST pl = (pLIST)p;
  
    // 对数据指针进行类型转换
    pLIST_DATA pLD = (pLIST_DATA) ( pl + 1 );
  
    delete pLD->p;
  }
  pLIST Head = NULL;
  
  void TestList()
  {
    pLIST_DATA d = new LIST_DATA;
    d->p = new char[24];
    strcpy( d->p, "Hello" );
    Head = AddToList( Head, (void *) d, sizeof( pLIST_DATA ),
    ListDataDestructor );
    // 该对象已被复制,现在删除原来的对象
    delete d;
  
    d = new LIST_DATA; 网管网www.bitscn.com
    d->p = new char[24];
    strcpy( d->p, "World" );
    Head = AddToList( Head, (void *) d, sizeof( pLIST_DATA ),
    ListDataDestructor );
    delete d;
  
    // 释放链表
    DeleteList( Head );
  }   
  在每个链表节点中包含同一个解除函数的同一个指针似乎是浪费内存空间。确实如此,但只有链表始终包含相同的对象才属于这种情况。按这种方式编写链表允许您将任何对象放在链表中的任何位置。大多数链表函数要求对象总是相同的类型或类。虚拟链表则无此要求。它所需要的只是将对象彼此区分开的一种方法。要实现这一点,您既可以检测解除函数指针的值,也可以在链表中所用的全部结构前添加一个类型值并对它进行检测。当然,如果要将链表编写为一个 C++ 类,则对指向解除函数的指针的设置和存储只能进行一次。
  
  C++ 解决方案:类链表
  本解决方案将 CList 类定义为从 LIST 结构导出的一个类,它通过存储解除函数的单个值来处理单个存储类型。请注意添加的 GetCurrentData() 函数,该函数完成从链表节点指针到数据偏移指针的数学转换。
  
  一个虚拟链表对象
  
   网管下载dl.bitscn.com
  // 定义解除函数指针
  
  typedef void (*ListNodeDestructor)( void * );
  
  // 未添加解除函数指针的链表
  
  typedef struct ndliststruct
  {
    ndliststruct *next;
  
  } ND_LIST, *pND_LIST;
  
  // 定义处理一种数据类型的链表类
  
  class CList : public ND_LIST
  {
  public:
    CList(ListNodeDestructor);
    ~CList();
    pND_LIST AddToList( void * data, size_t datasize );
    void *GetCurrentData();
    void DeleteList( pND_LIST Head );
  
  
  private:
    pND_LIST m_HeadOfList;
    pND_LIST m_CurrentNode;
    ListNodeDestructor m_DestructFunc;
  };
  
  // 用正确的起始值构造这个链表对象
  
  CList::CList(ListNodeDestructor Destructor)
    : m_HeadOfList(NULL), m_CurrentNode(NULL)
  {
    m_DestructFunc = Destructor;
  }
  
  // 在解除对象以后删除链表
  
  CList::~CList()
  {
网管联盟bitsCN_com

    DeleteList(m_HeadOfList);
  }
  
  // 向链表中添加一个新节点
  
  pND_LIST CList::AddToList( void * data, size_t datasize )
  {
  pND_LIST newlist=NULL;
  void *p;
  
  
    // 分配节点内存和数据内存
    newlist = (pND_LIST) malloc( datasize + sizeof( ND_LIST ) );
  
    // 为这块数据缓冲区指定一个指针
    p = (void *)( newlist + 1 );
  
    // 复制数据
    memcpy( p, data, datasize );
  
    // 将这个节点指定给链表的表头
    if( m_HeadOfList )
    {
      newlist->next = m_HeadOfList;
    }
    else
      newlist->next = NULL;
  
    m_HeadOfList = newlist;
  
    return m_HeadOfList;
  }
  
  // 将当前的节点数据作为 void 类型返回,以便调用函数能够将它转换为任何类型
  
  void * CList::GetCurrentData()
  {
    return (void *)(m_CurrentNode+1);
  }
  
  // 删除已分配的链表
网管网www_bitscn_com

  
  void CList::DeleteList( pND_LIST Head )
  {
    pND_LIST Next;
    while( Head )
    {
      Next = Head->next;
      m_DestructFunc( (void *) Head );
      free( Head );
      Head = Next;
    }
  }
  
  // 创建一个要在链表中创建和存储的结构
  
  typedef struct ListDataStruct
  {
    LPSTR p;
  
  } LIST_DATA, *pND_LIST_DATA;
  
  // 定义标准解除函数
  
  void ClassListDataDestructor( void *p )
  {
    // 对节点指针进行类型转换
    pND_LIST pl = (pND_LIST)p;
  
    // 对

TAGs
 上一篇:CCACHE改善协同构建时间加快编译   下一篇:Source Insight3.0:Linux源代码阅读
在 C/C++ 中如何构造通用的对象链表 评论:
loading.. 评论加载中…
评论:请自觉遵守互联网相关政策法规,评论不得超过250字。

验证码: 注册用户
本类热门排行:
最新推荐文章:
网管论坛交流: