欢迎访问 Lu程序设计
用C/C++设计Lu结构、字典、类等高级数据类型
1 说明
本文没有直接给出演示例子,而是对Lu系统扩展库LuSystem实现的结构、字典、类等高级数据类型做说明,但你仍然需要下载Lu32脚本系统,并下载软件包lu1code.rar,LuSystem的源代码就在这个软件包中,源代码中用到的Lu核心库函数仍然参考Lu编程指南。由于LuSystem是一个Lu扩展动态库,你可能还要参考Lu扩展动态库说明。
本文假定你已经阅读了本教程系列的前面的内容,对涉及到的Lu基本数据类型、Lu键树、以#开头的标识符(字符串)确定的唯一的整数等内容不再重复说明。
2 关于Lu系统扩展库LuSystem
LuSystem32.dll是一个Lu系统扩展动态库,包含一些增强Lu系统功能的函数、对系统内置类型的扩展以及一些新增数据类型等等。LuSystem不仅是对Lu系统的扩充,更在于演示Lu是极易扩充其功能的脚本,很少有脚本允许用户能方便地添加像字典、结构、类等高级的数据结构,但Lu允许,而且实现这些很容易,因而,LuSystem也是编程用户极佳的练手工具。
为了便于理解本文,可以先看一下头文件LuSystem32.h(包含了字典、结构、类等数据结构的定义)和该库的入口文件LuSystem32.cpp(定义了函数LuDll32,Lu扩展库唯一的输出函数)。
3 结构(struct)实现原理及难点分析
结构(struct)是一个线性表,可以存放若干任意的Lu数据。结构成员必须以#开头。结构成员的存储顺序(递增排列)与其定义顺序不一定相同,当然这并不影响使用。例如以下脚本代码:
!!!using("sys"); //使用命名空间sys
struct[#num, #姓名 : "luuu", #年龄].o[]; //函数o用于输出一个对象
结果(成员#num和#年龄没有赋值,故值为nil):
struct{#num : nil , #姓名 : luuu , #年龄 : nil}
从头文件LuSystem32.h中可找到的结构(struct)定义如下:
struct SStruct{ //结构 luINT max; //成员数目 luVOID *Name; //成员名字(递增排列以加快访问速度),由一个以#开头的标识符(字符串)确定的唯一的整数 LuData *Member; //成员 };
注意结构定义中,成员名字不是使用字符串,而是使用由该字符串确定的一个唯一整数来标识,显然这样访问速度更快,并且成员是递增排列的,更加快了访问速度;另外,结构成员是Lu基本数据类型,这样可以存储任意的数据,包括另一个结构对象,甚至是自身。
从结构的实现文件Struct.cpp中可以找到如下两个函数:
void _stdcall DelStruct(void *vStruct) //删除一个结构 { SStruct *pSStruct; pSStruct=(SStruct *)vStruct; if(pSStruct->Name) delete[] pSStruct->Name; if(pSStruct->Member) delete[] pSStruct->Member; delete pSStruct; } void _stdcall SignKeyStruct(void *me) //标记子键值(对象) { SStruct *pSStruct; luINT i; pSStruct=(SStruct *)me; for(i=0;i<pSStruct->max;i++) { pSignGoodObj(pSStruct->Member+i); //用函数SignGoodObj标记子对象 } }
函数DelStruct用于删除一个结构对象,但DelStruct是由锁定键的类型函数LockKey(LuSystem中使用的是函数指针pLockKey,下同,不再一一说明)注册到Lu系统中由Lu来调用的。函数SignKeyStruct是由插入键值函数InsertKey注册到Lu系统中由Lu来调用的,该函数的作用是:当Lu垃圾收集器工作时,如果一个结构是有效的,则该结构成员也应该是有效的,故Lu垃圾收集器会调用函数SignKeyStruct对结构成员做标记。顺便说一下,Lu垃圾收集器使用标记清除算法。
查看文件LuSystem32.cpp,会发现定义了全局变量keyStruct用以标识结构对象,该变量的值作为一个键被LockKey加锁(从其初始值keyStruct=-123115开始加锁,不成功时keyStruct--,直至加锁成功),加锁成功后,键keyStruct就只能存储结构对象。函数LockKey同时指明了销毁结构对象的函数是DelStruct;结构对象的运算符重载函数是OpLockStruct。另外,文件LuSystem32.cpp中还定义了标识对象keyStruct的字符串"struct",该字符串被注册为Lu整数常量,在Lu源代码中可用new(sys::struct,... ...)申请该对象。
再来看结构的实现文件Struct.cpp,结构对象的运算符重载函数OpLockStruct在该文件中定义,重载了函数set、len、copy、new、oset、oget、o等函数,相信你会在下面找到这些函数的具体实现,参考LuSystem的说明,可以更好地理解这些函数。为了便于操作结构对象,还向Lu系统注册(参考文件LuSystem32.cpp中的SetFunction函数)了函数sys::struct(对应C++函数lu_NewStruct)、sys::struct_oset(对应C++函数lu_osetStruct)和sys::struct_oget(对应C++函数lu_ogetStruct);当然,重载函数new、oset和oget也能实现对应的功能,因为它们对应相同的C++函数。
在扩展库LuSystem中测试了结构的效率。
4 字典(dict)实现原理及难点分析
字典(dict)是一个可直接存取的双向链表(使用双向链表便于删除一个字典元素),可以存放若干任意的Lu数据。字典元素由“键-值”对组成,键只能是字符串,但值可以是任何Lu数据类型,包括另一个字典,甚至自身。例如以下脚本代码生成了一个字典a并初始化,然后又在字典a中添加了一个元素:
!!!using("sys");
main(:a)= a=dict["aa":1.2, "abc":"luuu"], a."姓名"="王刚", o[a];
结果:
dict{aa : 1.2 , abc : luuu , 姓名 : 王刚}
从头文件LuSystem32.h中可找到的字典(dict)定义如下:
struct SDictItem{ //字典元素结构 wchar_t *KeyStr; //键 LuData KeyValve; //值 SDictItem *pAhead; //前一结点 SDictItem *pNext; //后一结点 }; struct SDict{ //字典结构 luKEY KeyDict; //标识一个字典,将锁定该键以存放字典元素 SDictItem MainSDI; //字典元素头结点 };
查看文件LuSystem32.cpp,会发现定义了全局变量keyDict用以标识字典对象,该变量的值作为一个键被LockKey加锁(从其初始值keyDict=-123114开始加锁,不成功时keyDict--,直至加锁成功),加锁成功后,键keyDict就只能存储字典对象。函数LockKey同时指明了销毁字典对象的函数是DelDict;字典对象的运算符重载函数是OpLockDict。另外,文件LuSystem32.cpp中还定义了标识对象keyDict的字符串"dict",该字符串被注册为Lu整数常量,在Lu源代码中可用new(sys::dict,...
...)申请该对象。
当创建一个字典(设字典指针为pSDict)时,先使用随机算法加锁一个私有键pSDict->KeyDict(参考Dict.cpp中的LockKeyDictItem函数),用于存放字典元素,然后将pSDict注册为keyDict类型的私有键。当往字典中存放字典元素(设字典元素指针为pSDictItem)时,将pSDictItem注册为pSDict->KeyDict类型的私有键,并将pSDictItem链接到pSDict->MainSDI。故定位字典元素a."me"(a为字典,"me"为字典元素)的操作为:根据a的指针和键keyDict找到a的结构(设为pSDict),然后根据字符串"me"和pSDict->MainSDI找到该字典元素。
从字典的实现文件Dict.cpp中可以找到如下三个函数:
void _stdcall DelDict(void *vDict) //删除一个字典 { SDict *pSDict; pSDict=(SDict *)vDict; pLockKey(pSDict->KeyDict,NULL,OpLockDictItem); //删除字典所有元素 delete pSDict; } void _stdcall DelDictItem(void *vSDI) //删除一个字典元素 { SDictItem *pSDI; pSDI=(SDictItem *)vSDI; pSDI->pAhead->pNext=pSDI->pNext; if(pSDI->pNext) pSDI->pNext->pAhead=pSDI->pAhead; if(pSDI->KeyStr) delete[] pSDI->KeyStr; delete pSDI; } void _stdcall SignKeyDict(void *me) //标记子键值(对象) { SDictItem *pSDI; pSDI=((SDict *)me)->MainSDI.pNext; while(pSDI) { pSignGoodObj(&(pSDI->KeyValve)); //用函数SignGoodObj标记子对象 pSDI=pSDI->pNext; } }
函数DelDict用于删除一个字典对象,函数DelDictItem用于删除一个字典元素对象,但DelDict和DelDictItem都是由锁定键的类型函数LockKey注册到Lu系统中由Lu来调用的。函数SignKeyDict是由插入键值函数InsertKey注册到Lu系统中由Lu来调用的,该函数的作用是:当Lu垃圾收集器工作时,如果一个字典是有效的,则该字典元素也应该是有效的,故Lu垃圾收集器会调用函数SignKeyDict对字典元素做标记。
文件Struct.cpp中定义了字典的运算符重载函数OpLockDict,重载了函数len、new、oset、oget、o等函数,相信你会在下面找到这些函数的具体实现,参考LuSystem的说明,可以更好地理解这些函数。为了便于操作字典对象,还向Lu系统注册(参考文件LuSystem32.cpp中的SetFunction函数)了函数sys::dict(对应C++函数lu_NewDict)、sys::dict_oset(对应C++函数lu_osetDict)和sys::dict_oget(对应C++函数lu_ogetDict);当然,重载函数new、oset和oget也能实现对应的功能,因为它们对应相同的C++函数。此外,还注册了函数sys::dict_del(对应C++函数lu_dict_del)、sys::dict_clear(对应C++函数lu_dict_clear)和sys::dict_reverse(对应C++函数dict_reverse)。
在扩展库LuSystem中测试了字典的效率。
5 类(class)实现原理及难点分析
类(class)是一个具有数据成员和方法成员的自定义数据结构。类可以继承和派生,类的层次结构是一棵树。
(1)类定义:class{... ...}
class{#cMyClass: #cBase1, #cBase2, ... //定义类cMyClass,继承自cBase1和cBase2, ... private: //私有数据成员 #pvMem1, #pvMem2, ... method: //私有方法(私有函数成员) #vmFun1 :@Fun1, #vmFun2 :@Fun2, ... public: //公有数据成员 #plMem1, #plMem2, ... method: //公有方法(公有函数成员) #__init__ : @init, #__del__ : @delme, #lmFun1 :@fun1, #lmFun2 :@fun2, ... };
类定义中,类名称、基类名称、成员及方法都是以#开头的标识符,不可重复。类名称在最前面,其次是基类名称;private和public的次序是任意的,且可多次定义;在private后定义的method为私有方法,在public后定义的method为公有方法,若method前面没有private和public,默认是公有方法;方法标识符后必须提供函数句柄。类成员的存储顺序与其定义顺序不一定相同。
#__init__和#__del__只可被定义为公有方法,其中#__init__用于定义构造函数,#__del__用于定义析构函数。若这两个方法被缺省,其函数句柄被自动指定为0。构造函数和析构函数只有一个参数,即对象本身。
若方法有不止一个参数,则方法被调用时,第一个参数总是传入对象本身,相当于C++的this指针。
约定:类名称以字母c开头;私有数据成员以pv开头;公有数据成员以pl开头;私有方法成员以vm开头;公有方法成员以lm开头。
(2)创建类对象:obj(pClass)
pClass是类定义的句柄。
创建类对象时,将自动调用每一个基类的构造函数#__init__初始化基类对象。对象被销毁时,将自动调用每一个基类的析构函数#__del__销毁基类对象。
语句 a.=b 将对象b的内容复制到对象a,要求a和b具有相同的类定义,若类定义中定义了方法#__copy__(有且只有2个参数),将自动调用该方法进行对象复制,否则仅依次复制类成员的值。
例如以下脚本代码:
!!!using("sys"); setA(p,x)= p.#a=x; //类方法定义 getA(p) = p.#a; //类方法定义 main(:a,b)= a=class{#A, //类定义 private: #a, //私有数据 public: method : #mGetA : @getA, //类公有方法 #mSetA : @setA //类公有方法 }, b=obj[a], //类对象 b.#mSetA=5, //通过类公有方法赋值 o[a, b, "\r\n", b.#mGetA, "\r\n"]; //输出类定义、类对象及类成员的值
结果:
class{#A : private: #a, method: public: method: #__init__ : 0, #__del__ : 0, #mGetA : 10838112, #mSetA : 10835952 } class obj{#A : private: #a : 5, public: } 5
从头文件LuSystem32.h中可找到的类(class)定义如下:
struct SClass{ //类定义 luVOID me; //类名字,是一个以#开头的标识符(字符串)确定的唯一的整数 luINT maxBaseClass;//基类数目 luVOID *BaseClass; //基类,是一些以#开头的标识符(字符串)确定的唯一的整数 luINT maxDMember; //数据成员数目 luINT maxMember; //成员数目 luVOID *Member; //成员(递增排列),是一些以#开头的标识符(字符串)确定的唯一的整数 char *attrMember; //成员属性。0:私有数据成员,1:公有数据成员,2:私有成员函数句柄,3:公有成员函数句柄 luVOID *indexMember;//数据成员序号(递增排列),或者成员函数句柄。仅对私有数据成员和公有数据成员进行编号,由该序号决定SClassObj的数据成员地址。 bool bFind; //用于发现循环继承 }; struct SClassObj{ //类对象 luVOID me; //类名字,是一个以#开头的标识符(字符串)确定的唯一的整数 SClassObj *ahead; //派生类(父类),使用前需要验证其有效性 luINT maxBaseClass;//基类数目 LuData *BaseClass; //基类 luINT maxDMember; //数据成员数目 LuData *DMember; //数据成员 };
查看文件LuSystem32.cpp,会发现定义了全局变量keyClass用以标识类定义,该变量的值作为一个键被LockKey加锁(从其初始值keyClass=-123116开始加锁,不成功时keyClass--,直至加锁成功),加锁成功后,键keyClass就只能存储类定义。函数LockKey同时指明了销毁类定义的函数是DelClass;类定义的运算符重载函数是OpLockClass。另外,文件LuSystem32.cpp中还定义了标识对象keyClass的字符串"class",该字符串被注册为Lu整数常量,在Lu源代码中可用new(sys::class,...
...)申请该对象。
在文件LuSystem32.cpp中还定义了全局变量keyClassObj用以标识类对象,该变量的值作为一个键被LockKey加锁(从其初始值keyClassObj=-123117开始加锁,不成功时keyClassObj--,直至加锁成功),加锁成功后,键keyClassObj就只能存储类对象。函数LockKey同时指明了销毁类对象的函数是DelClassObj;类对象的运算符重载函数是OpLockClassObj。另外,文件LuSystem32.cpp中还定义了标识对象keyClassObj的字符串"obj",该字符串被注册为Lu整数常量,在Lu源代码中可用new(sys::obj,... ...)申请该对象。
在Lu脚本中,p=class{... ...}创建一个类,根据返回的类句柄p用函数obj(p)创建类对象。class对应Class.cpp中的C++函数lu_NewClass;obj对应Class.cpp中的C++函数lu_NewClassObj。
在理解结构和字典的基础上,不难理解Class.cpp中定义的函数DelClass、SignKeyClass、OpLockClass、DelClassObj、SignKeyClassObj、OpLockClassObj,类定义重载函数new(lu_NewClass)、o(lu_oClass),以及类对象重载函数copy(lu_copyClassObj)、new(lu_NewClassObj)、oset(lu_osetClassObj)、oget(lu_ogetClassObj)、o(lu_oClassObj)等等。向Lu系统注册了函数除了sys::class、sys::obj外,还有sys::class_handle(对应C++函数lu_class_handle)、sys::obj_oset(对应C++函数lu_osetClassObj)和sys::obj_oget(对应C++函数lu_ogetClassObj)。
为了便于由类对象获得类的成员,文件Class.cpp中定义了函数GetClassMember,该函数使用缓冲池SClassBuf以快速定位类成员,同时实现了类成员的广度优先定位。最后,不难发现,还有两个函数FindClass和FreeClassObj;FindClass是在函数lu_NewClass中使用的,用于查找类是否循环定义及未定义;FreeClassObj是在函数lu_NewClassObj中使用的,用于释放一个类对象。
在扩展库LuSystem中测试了类的效率。
6 其他
你可能注意到了,我的联系方式就在下面,如有不明之处或有什么建议,可随时与我进行联系。
版权所有? Lu程序设计 2002-2013,保留所有权利
E-mail: forcal@sina.com QQ:630715621
最近更新: 2014年01月19日