欢迎访问 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)实现静态类对象的函数: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);

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日