Linux中国  设为主页
 收藏本站
 
当前位置: > 首页 ->Linux技术 ->Linux程序设计 ->在 C/C++中怎么样构造通用的对象链表
  相关分类: 
入门与提高
系统管理
网络应用
嵌入式系统
内核研究
服务器相关
发行版专区
Linux程序设计
Linux安全
BSD相关
桌面应用
  站内搜索: 
热门文章排行
热门文章排行 Linux系统下C语言编程 基础知识介绍 (05-01)
快速编辑Shell命令行(06-04)
Linux系统环境下的Socket编程详细解(04-19)
Awk 实例(一) (04-22)
基于libmad 的简单MP3流媒体播放器的(04-22)
精采文章排行
精采文章排行 快速编辑Shell命令行(06-04)
从2.4到2.6内核发展中的改进(06-04)
两个很详细的shell实例(06-04)
内核设计篇(06-04)
shell技巧(06-04)
  ·从2.4到2.6内核发展中的改进·两个很详细的shell实例·内核设计篇·shell技巧·批量添加用户·HowtoCreatingandBootingaNewKernelWitha·利用ip_conntrack表实现封ip的shell脚本,·30分钟搞定BASH脚本编程!·Shell初学者的入门知识

在 C/C++中怎么样构造通用的对象链表

作者:T. W. Burger    来源:IBM DW中国   点击:   日期:2007-04-22 [收藏] [投稿]

  IE是否经常中毒?推荐您

您是否做过这样一个项目,它要求您在内存中保存数目不定的若干不同对象?对于某些情况,二叉树是最佳选择,但在通常情况下,更简单的链表是显而易见的选择。

一个简化的问题示例

链表的难点在于必须复制链表处理函数来处理不同的对象,即便逻辑是完全相同的。例如:


两个结构类似的链表

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 代码解决方案:虚拟链表

此问题更好的解决方案之一是虚拟链表。虚拟链表是只包含链表指针的链表。对象存储在链表结构背后。这一点是这样实现的,首先为链表节点分配内存,接着为对象分配内存,然后将这块内存分配给链表节点指针,如下所示:


虚拟链表结构的一种实现

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 结构需要包含一个一般的解除函数指针,这个指针可用来在将节点从链表中删除并解除它之前释放内存(或者关闭文件,或者调用关闭方法)。


一个带有解除函数的链表

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;
}

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;
    d->p = new char[24];
    strcpy( d->p, "World" ); 
    Head = AddToList( Head, (void *) d, sizeof( pLIST_DATA ),
    ListDataDestructor );
    delete d;

    // 释放链表
    DeleteList( Head );
}       

在每个链表节点中包含同一个解除函数的同一个指针似乎是浪费内存空间。确实如此,但只有链表始终包含相同的对象才属于这种情况。按这种方式编写链表允许您将任何对象放在链表中的任何位置。大多数链表函数要求对象总是相同的类型或类。虚拟链表则无此要求。它所需要的只是将对象彼此区分开的一种方法。要实现这一点,您既可以检测解除函数指针的值,也可以在链表中所用的全部结构前添加一个类型值并对它进行检测。当然,如果要将链表编写为一个 C++ 类,则对指向解除函数的指针的设置和存储只能进行一次。

 如果您对本文有任何疑问或者建议,请到讨论区发表您的意见: >> 论坛入口 <<

上一页12 下一页

上一篇:Python anygui 项目预览   下一篇:用C语言实现Ping程序功能
文章评论】 【收藏本文】 【推荐好友】 【打印本文】 【我要投稿】 【论坛讨论

   相关文章:
·快速编辑Shell命令行

   文章评论:(1条)
  
 请留名: 匿名评论   点击查看所有评论 论坛讨论
 

 声明:刊登此文章是为了传递更多信息,文章内容仅供参考,转载请注明出处。