欢迎访问 Forcal程序设计

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）实现静态类对象的函数：Sta Person(: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);

5 静态类成员 [返回页首]

静态类成员只能有一个备份存在，不论这个类创建了多少个对象。每个对象共享这一变量备份。如下例：

> #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);

7 虚函数与多态性 [返回页首]

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日