动态网站制作指南 [  QQ表情  ]
[ 投票调查 ]
[ 企业邮箱 ]
[ 网站空间 ]
网络编程 | 站长之家 | 网页制作 | 图形图象 | 操作系统 | 冲浪宝典 | 软件教学 | 网络办公 | 邮件系统 | 网络安全 | 认证考试 | 系统进程
ASP源码 | .Net源码 | PHP源码 | JSP源码 | JAVA源码 | CGI源码 | VB源码 | C++源码 | Delphi源码 | PB源码 | VF源码 | 汇编 | 服务器
Firefox | IE | Maxthon | 迅雷 | 电驴 | BitComet | FlashGet | QQ | QQ空间 | Vista | 输入法 | Ghost | Word | Excel | wps | Powerpoint
asp | .net | php | jsp | Sql | c# | Ajax | xml | Dreamweaver | FrontPages | Javascript | css | photoshop | fireworks | Flash | Cad | Discuz!
当前位置 > 网站建设学院 > 网络编程 > C/C++教程
Tag:注入,存储过程,分页,安全,优化,xmlhttp,fso,jmail,application,session,防盗链,stream,无组件,组件,md5,乱码,缓存,加密,验证码,算法,cookies,ubb,正则表达式,水印,索引,日志,压缩,base64,url重写,上传,控件,Web.config,JDBC,函数,内存,PDF,迁移,结构,破解,编译,配置,进程,分词,IIS,Apache,Tomcat,phpmyadmin,Gzip,触发器,socket
网络编程:ASP教程,ASP.NET教程,PHP教程,JSP教程,C#教程,数据库,XML教程,Ajax,Java,Perl,Shell,VB教程,Delphi,C/C++教程,软件工程,J2EE/J2ME,移动开发
文章搜索服务
邮件订阅
输入你的邮件地址,
你将不会错过任何关于:
[ C/C++教程 ]的信息

本月文章推荐
.C++习题与解析(类和对象-07).
.TCP/IP网络重复型服务器通信软件.
.MFC中用正则表达式进行有效性验证.
.C++开发常用工具“群英会”.
.C语言入门之循环结构.
.C++学习:在C++中创建持久对象.
.C++数据结构学习:事件驱动模拟.
.在C程序中显示汉字.
.C++在网页设计中的应用.
.例程详析动态链接库.
.C++的中抽象.
.C++习题与解析-类和对象.
.CGI编程的安全性 -- 文件名.
.TCP/IP网络重复型服务器通信软件.
.COM Interop 理论与实践.
.编译器对标准库优化小测试.
.课程设计之打字练习程序.
.C语言初学者入门讲座 第十二讲 结.
.编程修养-C语言篇.
.C语言的程序结构.

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

发表日期:2008-3-8 |



  虚拟链表和类链表可以很好地实现这一点

T. W. Burger
Thomas Wolfgang Burger Consulting公司的老板
2000 年 9 月
内容:
简化的问题
C 代码解决方案
C++ 解决方案
小结
参考资源
作者简介


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

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

两个结构类似的链表

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++ 类,则对指向解除函数的指针的设置和存储只能进行一次。

C++ 解决方案:类链表
本解决方案将 CList 类定义为从 LIST 结构导出的一个类,它通过存储解除函数的单个值来处理单个存储类型。请注重添加的 GetCurrentData() 函数,该函数完成从链表节点指针到数据偏移指针的数学转换。

一个虚拟链表对象

// 定义解除函数指针

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()
{
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);
}

// 删除已分配的链表

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;

// 对数据指针进行类型转换
pND_LIST_DATA pLD = (pND_LIST_DATA) ( pl + 1 );

delete pLD->p;
}

// 测试上面的代码

void MyCListClassTest()
{
// 创建链表类

CList* pA_List_of_Data = new CList(ClassListDataDestructor);

// 创建数据对象

pND_LIST_DATA d = new LIST_DATA;
d->p = new char[24];
strcpy( d->p, "Hello" );

// 创建指向链表顶部的局部指针

pND_LIST Head = NULL;

//向链表中添加一些数据

Head = pA_List_of_Data->AddToList( (void *) d,
sizeof( pND_LIST_DATA ) );
// 该对象已被复制,现在删除原来的对象
delete d;

// 确认它已被存储
char * p = ((pND_LIST_DATA) pA_List_of_Data->GetCurrentData())->p;

d = new LIST_DATA;
d->p = new char[24];
strcpy( d->p, "World" );
Head = pA_List_of_Data->AddToList( (void *) d, sizeof( pND_LIST_DATA ) );
// 该对象已被复制,现在删除原来的对象
delete d;

// 确认它已被存储
p = ((pND_LIST_DATA) pA_List_of_Data->GetCurrentData())->p;

// 删除链表类,析构函数将删除链表
delete pA_List_of_Data;
}



小结
从前面的讨论来看,似乎仅编写一个简单的链表就要做大量的工作,但这只须进行一次。很轻易将这段代码扩充为一个处理排序、搜索以及各种其他任务的 C++ 类,并且这个类可以处理任何数据对象或类(在一个项目中,它处理大约二十个不同的对象)。您永远不必重新编写这段代码。


参考资源

* The Linux C Programming Lists 旨在帮助人们用 C 语言进行 Linux 编程,其中包括许多到邮件列表、常见问题解答、教程以及其他内容的链接。
* Microsoft Foundation Class 在其 CList 类中提供了类似的功能。MFC 中的 CList 以及别的类要求类型或类只能是一种类型,程序员对代码没有控制权,这一点与从零开始构建链表不同。

作者简介
Thomas Wolfgang Burger 是 Thomas Wolfgang Burger Consulting 公司的老板。自 1978 年以来,他做过咨询人员、教师、分析员和应用程序开发员。可以通过 twburger@bigfoot.com 与他联系。

您对这篇文章的看法如何?

真棒! 好文章 一般,尚可 需提高 太差!

意见


(c) Copyright IBM Corp. 2001, (c) Copyright IBM China 2001, All Right Reserved
隐私 法律 联系
上一篇:下面的源码中搬掉僵尸 人气:288
下一篇:编程修养-C语言篇 人气:404
浏览全部C/C++的内容 Dreamweaver插件下载 常用网页广告代码全集
  最新网站源码 最新软件下载
2008-9-4 LPLY CMS 网站管理系统 v5.0
2008-9-4 缤纷互动视频交友 v3.01.902
2008-9-4 ADN视频收藏专家 v3.0 bulid 080
2008-9-4 天空网络电影系统SKYUC v2.5.6 简
2008-9-4 Web Wiz Rich Text Editor(文本编
2008-9-4 幻影动漫网视频系统(Ppdong) v1.
2008-9-4 乐维电脑在线DIY配置系统
2008-9-4 老樊文章管理系统SQL版
2008-9-4 ASP.NET 2.53 缩略图水印组件源码
2008-8-23 Mini WinMount V0.4
2008-8-23 Vista优化大师3.11正式版
2008-8-23 Wine 1.13
2008-8-23 KlipFolio 5.0 Build 5899-80
2008-8-23 Windows Sysinternals Desktops
2008-8-23 OneTap Movies1.2破解版
2008-8-23 AnnotaterPDF阅读1.1.503 破解版
2008-8-23 SoundMeter分贝测量仪 v1.0汉化破
2008-8-23 iDrum音乐节拍1.0破解版
  发表评论
姓 名: 验证码:
内 容:
站长工具:网站收录查询 | Google PR查询 | ALEXA排名查询 | CSS在线编辑器 | 广告代码 | Html转换js | js/vbs加密 | md5加密 | 进制转换
实用工具:汉字翻译拼音 | 符号对照表 | 个税计算 | 经典小工具 | 汉字简繁转换 | 普通单位换算 | 公制单位换算 | 生辰老黄历 | 国内电话区号 国家代码与域名缩写 | 文字加密解密 | 健康查询 | 万年历 | 汉字横竖排版 | 手机号码查询 | 计算器 | ip搜索
业务联系 | 广告刊登 | 频道合作 | 投稿荐稿 | 联系方式 | 加入收藏 | RSS订阅
Copyright © 2000-2008 www.knowsky.com All rights reserved | 网络实名:动态网站制作指南 | 沪ICP备05001343号
ホームページ制作 不動産検索システム 求人情報
防水工事·改修工事 フットサル大会 探偵