FORCAL中的对象、函数和模块
目 录
1
Forcal中的对象 2 Forcal中的函数 3 Forcal中的模块 4 Forcal中的类模块 |
5
静态类成员 6 类成员函数 7 虚函数与多态性 |
Forcal的核心库Forcal32W.dll中没有类和对象的概念,但Forcal32W.dll对功能扩展提供了很好的支持,利用Forcal32W.dll本身提供的编程功能和Forcal扩展动态库FcData及MForcal(Forcal模块化编译运行库),很容易地构造出类及对象的概念。由于动态编译的特点,Forcal完全可以看作是面向对象的。
1 Forcal中的对象 [返回页首]
使用FcData中的类定义可以创建对象,实际上,在Forcal中一切数据都可以看作是对象,而任何一个函数或表达式都可以看作是类或对象的成员函数。对对象进行合理的分类可以更好地使用它们。 根据使用特点,Forcal中的对象可以分为三类:Forcal内置数据对象、非FcData类对象、FcData类对象。
1.1 Forcal内置数据对象
Forcal中有三种表达式:整数、实数和复数表达式,分别对整数、实数和复数数据进行运算处理,整数、实数和复数即Forcal的三种内置数据对象。Forcal内置数据对象的成员函数是一些常用的数学函数或自定义表达式。Forcal内置数据对象可以直接存取,而且可以用等号运算符进行赋值。例如:
(2).sin(); //sin(2);
x.cos().sin(); //sin[cos(x)];
x=(2).sin(); //x=sin(2);
1.2 非FcData类对象
FcData中所有的数据都用函数new申请(或者用newcopy创建数据的副本),这些数据可以是简单数据、数组、类及外部数据类型等,所有的数据都用一个指针,即一个4字节整数进行标识。所有的FcData数据都是对象。FcData对象只能用专用的成员函数进行存取,不能用等号运算符进行赋值。如果一个函数或表达式遇到不可识别的FcData对象,将返回一个运行错误。
FcData对象可分为两类:非FcData类对象和FcData类对象。进行这种分类的依据仅仅是因为FcData类对象是一种特殊的数据。
除了FcData之外,其他Forcal扩展动态库中也有一些函数可以创建对象,这些对象也是非FcData类对象。非FcData类对象也包括Forcal内置字符串。简单地讲,除了Forcal内置数据对象和FcData类对象,其余的都是非FcData类对象。例如:
i: (:x)= x=new(int), //申请一个整数对象x
x.set(99), //调用整数对象的成员函数set给x赋值
x.get(); //通过整数对象的成员函数get获得x的值
i: "Hello !".printff[]; //字符串对象及成员函数调用
注意:除了Forcal内置数据对象以外,其他的对象只能用专用的成员函数进行存取,不能用等号运算符进行赋值。非FcData类对象和FcData类对象都是如此。
1.3 FcData类对象
FcData类由关键字class定义,如:
person=new(class,EndType:"姓名","性别","年龄"); //定义一个类,描述一个人的简单信息,有三个类成员
给类成员赋值:
person."姓名".CSetP[new(wchar_s,EndType,"王强")];
person."性别".CSetP[new(wchar_s,EndType,"男")];
person."年龄".CSetP[new(int,23)];
也可以用函数CSetItem一次给多个成员赋值:
person.CSetItem{
"姓名",new(wchar_s,EndType,"王强"),
"性别",new(wchar_s,EndType,"男"),
"年龄",new(int,23)
};
获得并输出类成员的值:
printff["{1,s},{2,s},{3,i}岁.\r\n",person.CSB("姓名"),person.CSB("性别"),person.CSB("年龄").get()];
完整的例子如下所示:
i:(::person)= person=new(class,EndType:"姓名","性别","年龄"); //定义一个类,描述一个人的简单信息,有三个类成员
i:(::person)=
{
person."姓名".CSetP[new(wchar_s,EndType,"王强")],
person."性别".CSetP[new(wchar_s,EndType,"男")],
person."年龄".CSetP[new(int,23)]
};
i:(::person)= printff["{1,s},{2,s},{3,i}岁.\r\n",person.CSB("姓名"),person.CSB("性别"),person.CSB("年龄").get()];
FcData中类的成员都是公有数据,每一个数据可看作一个对象,所有的数据都通过特定的函数进行存取。要想使用私有数据和函数,把类放到模块(称类模块)中就可以了。FcData中的类及成员之间有自然的继承性,而且可以通过循环链表实现相互继承。FcData中的类层次关系是一棵树或一个连通图。缺省情况下,在FcData中销毁一个类对象时,所有的基类对象也一并销毁,在类对象以循环链表的方式相互继承的情况下也是 如此。
2 Forcal中的函数 [返回页首]
本文中,为了说明的方便,我们将自定义的表达式也称为函数。
可按不同的分类方法对Forcal中的函数进行分类。
按所处理的数据类型不同,可分为整数函数、实数函数和复数函数三种,分别存在于整数表达式、实数表达式和复数表达式中。
按来源不同,可分为Forcal内置函数(包括一级函数和二级函数)、外部二级函数(通过Forcal扩展模块添加的函数)、用户自定义函数。
按运算速度不同可分为一级函数和二级函数,一级函数都是Forcal内置的,二级函数有些是内置的,但更多的是外置的。
按是否存在递归可分为递归函数和非递归函数。
按函数属性可分为模块私有函数和模块公有函数,私有函数只能被本模块的表达式所访问,公有函数可被任意模块的函数所访问。
按所处理的对象不同,称为某种对象的成员函数。
本文主要讨论对象的成员函数。如果某个成员函数不能处理所传入的对象参数,将产生一个运行错误。
2.1 可处理所有对象的函数
纯粹的数值运算函数,如sin、cos等,把所有的对象都看作一个数值来进行运算,因而这类函数可处理所有的对象,是所有对象的成员函数。
2.2 处理特定对象的函数
某些函数只能处理一些特定的对象,是这些对象的专用成员函数。如Forcal扩展库OpenFcGl中的函数gluSphere,只能处理二次曲面对象,因而gluSphere是二次曲面对象的专用成员函数。
2.3 处理某些对象的函数
有些函数能处理多种类型的对象,如函数printff可处理Forcal内置字符串对象和FcData字符数组对象,函数copy可处理绝大多数的FcData数据对象。
3 Forcal中的模块 [返回页首]
Forcal模块化编译运行库MForcal可对源代码进行模块化编译,例如:
#MODULE#
//定义一个子模块,模块名为Module1;
i:set(x::add)= add=x;
//模块号为负,只能被该模块的表达式所访问;
i:get(::add)= add;
//模块号为负,只能被该模块的表达式所访问;
~i:Module1_set(x)= set(x);
//模块号为正,任意模块包括本模块均可访问;
~i:Module1_get()= get();
//模块号为正,任意模块包括本模块均可访问;
#END#
//子模块定义结束,可缺省;
#MODULE#
//定义一个子模块,模块名为Module2;
i:set(x::add)= add=x;
//模块号为负,只能被该模块的表达式所访问;
i:get(::add)= add;
//模块号为负,只能被该模块的表达式所访问;
~i:Module2_set(x)= set(x);
//模块号为正,任意模块包括本模块均可访问;
~i:Module2_get()= get(); //模块号为正,任意模块包括本模块均可访问;
#END#
//子模块定义结束,不可缺省;
i:set(x::add)= add=x;
//主模块中的表达式。
i:get(::add)= add;
//主模块中的表达式。
i:set(33);
//主模块中的表达式。
i:Module1_set(11); //主模块中的表达式。
i:Module2_set(22); //主模块中的表达式。
i:Module1_get(); //主模块中的表达式。
i:Module2_get(); //主模块中的表达式。
i:get();
//主模块中的表达式。
建议模块公有表达式的命名格式为模块名加函数名,即:ModuleName_FunctionName。 如果采用模块命名空间,则输出函数可以采用较短的函数名。
4 Forcal中的类模块 [返回页首]
FcData类和MForcal模块结合称为类模块,可实现更为强大的功能。
在类模块(假设名称为:Person)中一般应 实现以下三个函数:
1)初始化模块函数:InitPerson(:static,... ...)
该函数为私有函数,允许自动执行,更好的实现是设置为编译后立即执行(表达式名称前加编译符“!”),且仅需执行一次。
2)实现动态类对象的函数:NewPerson()
该函数为全局函数,一般设置该函数不能自动执行,只能被其他表达式调用。
每次调用该函数,均创建一个类对象。由NewPerson()返回的对象p,不用时须调用函数delete(p)销毁 ,因此称为动态类对象。
3)实现静态类对象的函数:StaPerson(:static,CPerson,nFree)
该函数为全局函数,一般设置该函数不能自动执行,只能被其他表达式调用执行。
每次调用该函数,返回一个静态类对象。所谓静态类对象是指该类对象只有一个,没有其他的副本。静态类对象由该函数管理,无需而且不能在函数的外部销毁,也就是说,在函数的外部只能使用它,但不能销毁它。一般情况下,每次调用该函数,将返回同一个类指针,除非在函数的外部执行了函数DelAllFCD。
该函数的实现思想:在初始化过程中,申请类对象CPerson,并将当前调用函数DelAllFCD()的次数保存到静态变量nFree中,该次数由函数DelAllFCDNum()获得。由于用户可在任意的时刻用函数DelAllFCD()销毁所有的FcData数据,则类 对象CPerson也将被销毁。比较两次调用函数DelAllFCDNum()的返回值,可以知道类 对象CPerson在这两次调用期间是否被销毁,若两个返回值相等,表示没有销毁,否则已被销毁。为了以后能检测CPerson是否被销毁,在初始化函数中需用静态变量nFree保存函数DelAllFCDNum()的返回值。若类 对象CPerson被销毁,需要重新申请类 对象。函数每次都返回CPerson。
为了在销毁表达式时能自动销毁对象CPerson,须用自动静态变量保存类对象。当然这并不是必须的,因为FcData有自动垃圾回收的功能。
4.1 Person类的模块实现
#MODULE# //定义一个模块,输出类Person,描述一个人的姓名、性别和年龄
~i::NewPerson(:person)= //申请类Person的对象,该函数不会自动执行,全局函数
{
person=new(class,EndType:"姓名","性别","年龄"),
person."姓名".CSetP[new(wchar_s,11)],
person."性别".CSetP[new(wchar_s,3)],
person."年龄".CSetP[new(int)],
person
};#END#
i:(::person)=
{
person=NewPerson(),
person.CSB("姓名").FCDstrcpy("王强",false),
person.CSB("性别").FCDstrcpy("男",false),
person.CSB("年龄").set(22)
};
i:(::person)= printff["{1,s},{2,s},{3,i}岁.\r\n",person.CSB("姓名"),person.CSB("性别"),person.CSB("年龄").get()];
i:(::person)= delete(person);
4.2 Student类的模块实现,继承自Person类
#MODULE# //定义一个模块,输出类Person,描述一个人的姓名、性别和年龄
~i::NewPerson(:person)= //申请类Person的对象,该函数不会自动执行,全局函数
{
person=new(class,EndType:"姓名","性别","年龄"),
person."姓名".CSetP[new(wchar_s,11)],
person."性别".CSetP[new(wchar_s,3)],
person."年龄".CSetP[new(int)],
person
};#END#
#MODULE# //定义一个模块, 输出类Student,描述一个学生的信息,继承自Person类
~i::NewStudent(:student:CStudent,nFree)= //申请类Student的对象,该函数不会自动执行,全局函数
{
student=new(class,EndType:"Person","班级","爱好"),
student."Person".CSetP[NewPerson()],
student."班级".CSetP[new(wchar_s,10)],
student."爱好".CSetP[new(wchar_s,10)],
student
};#END#
i:(::student)=
{
student=NewStudent(),
student.CSB("姓名").FCDstrcpy("王强",false),
student.CSB("性别").FCDstrcpy("男",false),
student.CSB("年龄").set(22),
student.CSB("班级").FCDstrcpy("二年级",false),
student.CSB("爱好").FCDstrcpy("足球",false)
};
i:(::student)= printff["{1,s},{2,s},{3,i}岁,{4,s},{5,s}\r\n",student.CSB("姓名"),student.CSB("性别"),student.CSB("年龄").get(),student.CSB("班级"),student.CSB("爱好")];
i:(::student)= delete(student);
静态类成员只能有一个备份存在,不论这个类创建了多少个对象。每个对象共享这一变量备份。如下例:
#MODULE# //定义一个模块, 输出类Person,描述一个人的姓名、性别 、年龄和省份,其中省份是静态类成员
i::NewProvince(:n,static,province,free,nFree:)= //定义 静态数据Province,将成为类Person的静态类成员,该函数是私有函数,不会自动执行
{
if[free,delete(province),return(0)], //销毁表达式时,自动销毁申请的对象
if{!static,static=1,nFree=-1}, //初始化时nFree=-1
n=DelAllFCDNum(),
if{ nFree!=n, //如果静态数据Province已被函数DelAllFCD()销毁,重新 申请
nFree=n,
province=new(wchar_s,EndType,"山东省")
},
province
};~i::NewPerson(:person:CPerson,province,nFree)= //申请类Person的对象,该函数不会自动执行,全局函数
{
person=new(class,EndType:"姓名","性别","年龄","省份"),
person."姓名".CSetP[new(wchar_s,11)],
person."性别".CSetP[new(wchar_s,3)],
person."年龄".CSetP[new(int)],
person."省份".CSetP[NewProvince()], //给类成员"省份"赋值
person."省份".CSetA[false], //设置类成员"省份"的属性为不能自动删除,即成为静态类成员
person
};#END#
i:(::person)=
{
person=NewPerson(),
person.CSB("姓名").FCDstrcpy("王强",false),
person.CSB("性别").FCDstrcpy("男",false),
person.CSB("年龄").set(22)
};
i:(::person)= printff["{1,s}",person.CSB("姓名")];
i:(::person)= printff["{1,s}",person.CSB("性别")];
i:(::person)= person.CSB("年龄").get();
i:(::person)= printff["{1,s}",person.CSB("省份")];i:(::person,p)=
{
p=NewPerson(),
copy(p,person)
};
i:(::person)= person.CSB("姓名").FCDstrcpy("李平",false);
i:(::person)= person.CSB("省份").FCDstrcpy("北京市",false);i:(::person)= printff["{1,s},{2,s},{3,i}岁,{4,s}\r\n",person.CSB("姓名"),person.CSB("性别"),person.CSB("年龄").get(),person.CSB("省份")];
i:(::person)= delete(person);i:(::p)= printff["{1,s},{2,s},{3,i}岁,{4,s}\r\n",p.CSB("姓名"),p.CSB("性别"),p.CSB("年龄").get(),p.CSB("省份")];
i:(::p)= delete(p);
6 类成员函数
FcData中定义的类没有成员函数的概念。但如果一个模块输出了一个类,则可以使用模块命名空间中的函数作为类的成员函数。如下例:
#MODULE# //定义模块Int2Op,对两个整数进行运算
!Module("Int2Op"); //创建模块命名空间Int2Op
~i::NewInt2Op(:Int2Op)= //申请类Int2Op的对象,该函数不会自动执行,全局函数
{
Int2Op=new(class,EndType:"x","y"),
Int2Op."x".CSetP[new(int)],
Int2Op."y".CSetP[new(int)],
Int2Op
};i:add(p)= //计算对象Int2Op的两个成员"x"和"y"的和
{
p.CSB("x").get()+p.CSB("y").get()
};i:sub(p)= //计算对象Int2Op的两个成员"x"和"y"的差
{
p.CSB("x").get()-p.CSB("y").get()
};!OutFun("add","sub"); //输出模块命名空间中的表达式
#END#
i: (::a)= a=NewInt2Op(), a.CSB("x").set(2), a.CSB("y").set(5); //主模块中的表达式
i: (::a)= a.Int2Op::add(); //主模块中的表达式
i: (::a)= a.Int2Op::sub(); //主模块中的表达式
i: (::a)= delete(a);
Forcal是动态编译运行的,多态性是Forcal是一个基本特征。通过虚函数实现运行时的多态性给程序提供了更大的灵活性。
7.1 整数运算的例子
#MODULE# //定义模块Int2Op,对两个整数进行运算
!Module("Int2Op"); //创建模块命名空间Int2Op
~i::NewInt2Op(:Int2Op)= //申请类Int2Op的对象,该函数不会自动执行,全局函数
{
Int2Op=new(class,EndType:"x","y"),
Int2Op."x".CSetP[new(int)],
Int2Op."y".CSetP[new(int)],
Int2Op
};i:add(p)= //计算对象Int2Op的两个成员"x"和"y"的和
{
p.CSB("x").get()+p.CSB("y").get()
};i:sub(p)= //计算对象Int2Op的两个成员"x"和"y"的差
{
p.CSB("x").get()-p.CSB("y").get()
};!OutFun("add","sub"); //输出模块命名空间中的表达式
#END#
#MODULE# //定义模块Int3Op,继承自Int2Op,对三个整数进行运算,其中两个整数来自基类Int2Op
!Module("Int3Op","Int2Op"); //创建模块命名空间Int2Op,继承自Int2Op
~i::NewInt3Op(:Int3Op:CInt3Op,add,sub)= //申请类Int3Op的对象,该函数不会自动执行,全局函数
{
Int3Op=new(class,EndType:"Int2Op","z"),
Int3Op."Int2Op".CSetP[NewInt2Op()],
Int3Op."z".CSetP[new(int)],
Int3Op
};i:add(p)= //计算对象Int3Op的三个成员"x"、"y"和"z"的和,其中"x"和"y"来自基类Int2Op
{
p.CSB("x").get()+p.CSB("y").get()+p.CSB("z").get()
};i:sub(p)= //计算对象Int3Op的三个成员"x"、"y"和"z"的差,其中"x"和"y"来自基类Int2Op
{
p.CSB("x").get()-p.CSB("y").get()-p.CSB("z").get()
};!OutFun("add","sub"); //输出模块命名空间中的表达式
#END#
i: (::a)= a=NewInt3Op(), a.CSB("x").set(2), a.CSB("y").set(5), a.CSB("z").set(8); //主模块中的表达式
i: (::a)= a.Int3Op::add(); //主模块中的表达式 ,调用派生类的成员函数add
i: (::a)= a.Int3Op::sub(); //主模块中的表达式 ,调用派生类的成员函数sub
i: (::a)= a.Int3Op::Int2Op::add(); //主模块中的表达式 ,调用基类的成员函数add
i: (::a)= a.Int3Op::Int2Op::sub(); //主模块中的表达式 ,调用基类的成员函数sub
i: (::a)= delete(a);
7.2 几何图形面积的例子
在这个例子中,创建了一个基类Area,它是一个几何图形的二维面积。同时还创建了一个虚函数GetArea(),当派生类覆盖GetArea()时,GetArea()返回派生类定义的几何形状的面积。该程序计算了一个矩形的面积。为了简单,该程序使用整数进行计算。
#MODULE# //类模块Area,有两个数据成员"dim1"和"dim2",三个函数成员SetArea、GetDim和GetArea,其中GetArea为虚函数,在派生类中要覆盖该函数
!Module("Area"); //创建模块命名空间Area
~i::NewArea()= //申请类Area的对象,该函数不会自动执行,全局函数
{
new[class,EndType:"dim1","dim2"].CSetItem{
"dim1",new(int),
"dim2",new(int)
}
};i:SetArea(p,d1,d2)= //设置对象Area的两个成员"dim1"和"dim2"的值
{
p.CSB("dim1").set(d1), p.CSB("dim2").set(d2)
};i:GetDim(p,d1,d2)= //得到对象Area的两个成员"dim1"和"dim2"的值
{
p.CSB("dim1").get(&d1), p.CSB("dim2").get(&d2)
};i:GetArea(p)= //得到对象Area表示的图形面积,虚函数,派生类要覆盖该函数
{
printff("\r\n这是个虚函数,派生类必须覆盖该函数!\r\n")
};!OutFun("SetArea","GetDim","GetArea"); //输出模块命名空间中的表达式
#END#
#MODULE# //类模块Rectangle,继承自Area,覆盖了基类的成员函数GetArea
!Module("Rectangle":"Area"); //创建模块命名空间Rectangle,继承自Area
~i::NewRectangle()= //申请类Rectangle的对象,该函数不会自动执行,全局函数
{
new[class,EndType:"Area"].CSetItem{
"Area",NewArea()
}
};i:GetArea(p:d1,d2)= //得到对象Area表示的矩形的面积,覆盖了基类的同名函数
{
p.Area::GetDim(&d1,&d2), //调用基类的成员函数GetDim,获得图形尺寸
d1*d2
};!OutFun("GetArea"); //输出模块命名空间中的表达式
#END#
i: (::a)= a=NewRectangle(), a.Rectangle::SetArea(2,3); //主模块中的表达式,调用基类的成员函数SetArea
i: (::a)= a.Rectangle::GetArea(); //主模块中的表达式,调用派生类的成员函数GetArea
i: (::a)= a.Rectangle::Area::GetArea(); //主模块中的表达式,调用基类的成员函数GetArea
i: (::a)= delete(a);
版权所有© Forcal程序设计
2002-2010,保留所有权利
E-mail: forcal@sina.com
QQ:630715621
最近更新:
2010年01月23日