Chinaunix首页 | 论坛 | 博客
  • 博客访问: 68028
  • 博文数量: 13
  • 博客积分: 1410
  • 博客等级: 上尉
  • 技术积分: 145
  • 用 户 组: 普通用户
  • 注册时间: 2007-12-09 19:43
文章分类

全部博文(13)

文章存档

2009年(5)

2008年(8)

我的朋友

分类: C/C++

2009-02-26 16:39:58

引文:

以下内容引自高焕堂老师的《》一书。

认识C的对象与类

类的用途

    类的目的是﹕创造新数据型态。为那描述自然界的万事万物,必须有各式各样的数据型态,才足以充分贴切地表达自然界的静态与动态的美。C只提供数种基本数据型态,欲表达人类社会或大自然的景象,实在并不足够。

    然而C加上(Class)概念之后,就很容易解决这个问题了。它让程序师定义与创造自己心爱的数据型态来描述心中所想的、眼睛所看到的任何自然界景象。

    C里,int double char等常称为基本数据型态(Fundamental Data Type) ﹔藉类而创造出来的数据型态则称为抽象数据型态(Abstract Data Type)抽象意谓着﹕类只描述自然事物的重要(Essential) 特征和行为,而忽略不重要的细节。于是,有个不成文的规则是﹕

      由基本数据型态所宣告的变量,直称为变量。

      由抽象数据型态(类)所宣告的变量,特称为对象。

 

此规则的目的是﹕让已受古典程序熏陶的C 程序师,能区别C OOPC的不同。如果您对软件的认识才刚起步,宜把变量和对象视为同义词,这是OOP 的本质,只因C++ C 演变而来,担负了新旧传承的任务,才加以区分的。

    例如﹕定义类如下﹕

          CLASS(Rose)

             {

               ....

             };

 

Rose就是我们新创的数据型态,将用来生成对象,以描述自然界的玫瑰花。于是可宣告对象如下﹕

          Rose a;

          pr = RoseNew();

 

pa 是指向对象的指针,*pa 的型态是Rose。因对象就是变量,所以其在内存中也占有空间,裨储存数据。

 

            ┌───────────────┐

                      ╭───╮         

                对象 a                (主存储器)

                      ╰───╯         

                                         

            └───────────────┘

 

定义类

    类是一群具有共同重要特性的对象。类的定义就是说明这群对象具有什么的重要特性。特性包括对象的特征及行为。软件中的对象以数据来表达特征,以函数来表达行为。因此,类的定义就是说明软件中的对象,应含那些数据及那些函数。定义类时,应考虑如下﹕

 

我们欲描述那些对象﹖

    如果欲描述手中的一朵花,而此朵花是一朵(is a)玫瑰花,则可得知手上的花是对象,而玫瑰花是类。为了描述手上的玫瑰花,就得定义类叫Rose

 

对象有那些重要特征(Attribute)

    如果您想描述其价钱,也想描述其最适合做那月份的生日花﹔则可知Rose类应包含两项重要数据──price month 。于是,就可运用lw_oop.h宏来定义Rose类,并生成对象了,如下﹕

 

#include "stdio.h"

#include "lw_oopc.h"

 

CLASS(Rose)

    {

        float price;

        int   month;

    };

 

void main()

    {

      Rose rose;

      rose.price = 20.5;

      rose.month = 6;

      printf("price= %6.2f\n", rose.price);

      printf("month= %d\n", rose.month);

      getchar();

      return 0;

    }

 

Rose类的对象rose在内存中占了一块空间,内含两个属性──pricemonth,如下﹕

 

              rose.price   rose.month

             ╭──────┬──────╮

  rose对象     20.5          6      

             ╰──────┴──────╯

 

此对象代表手上那朵花,而pricemonth两个属性描述着该朵花的特征。

    如果桌上有一朵花,且它是一朵(is a)玫瑰花,则它与手上的花属于同一类,可直接拿Rose类来宣告对象,以描述桌上那朵花,如下﹕

 

#include

#include "stdio.h"

#include "lw_oopc.h"

 

CLASS(Rose)

    {

        float price;

        int month;

    };

 

void main()

    {

      Rose rose1, rose2;

      rose1.price = 20.5;

      rose1.month = 6;

      rose2.price = 1.25;

      rose2.month = 7;

 

      printf("%6.1f, %d\n", rose1.price, rose1.month);

      printf("%6.1f, %d\n", rose2.price, rose2.month);

      getchar();

      return 0;

    }

 

     Rose类就像过年过节时,妈妈做粿时的粿印,同一个模子(Template)可印出许多同样形状的粿。所以Rose是粿印,而 *pr1 *pr2 是真实可吃的粿。计算机的主存储器就像妈妈用的蒸笼

 

┌──────────────────────────────────┐

    rose1.price  rose1.month         rose2.price  rose2.month      

  ╭──────┬──────╮    ╭──────┬─────╮   

    20.5           6              1.25        6        

  ╰──────┴──────╯    ╰──────┴─────╯   

           rose1对象                        rose2对象             

└──────────────────────────────────┘

                        (主存储器)

 

对象有那些重要行为﹖

    前述Rose的特征──month , 并非自然界玫瑰花与生俱来的,而是人们所赋予的情意。所以对象并非单纯地描述自然界天生俱有的特性,也包括人们赋予的抽象涵意。同样地,软件中的对象除了 描述自然界对象的行为外,也会描述人们所赋予的特殊行为。例如,自然界有石头、水牛和太阳,则软件中有石头、水牛和太阳对象来描述的,但软件中的石头会点 头、水牛会弹琴、太阳会撒娇等。这是所谓的对象人性化,软件师在创造对象时,可尽情地把对象想象成绝顶聪明的。例如,Rose的对象,可能具有如下行为﹕

 

        散发罗曼情意。

        说出它代表何人的心意。

        说出它的价钱。

        正在盛开或凋谢。

        飞过秋千去。

          .....

 

因此,赋予人性后,Rose的对象将比实际玫瑰花更加罗曼帝克呢﹗假设我们认为Rose的重要行为是──

说出它的颜色

 

Rose类应增加一个函数──say() 如下﹕

#include "stdio.h"

#include "lw_oopc.h"

 

CLASS(Rose)

    {

        float price;

        int month;

void (*say)();

   };

 

static void say()

    { printf("price is RED\n"); }

 

 

void main()

    {

      Rose rose;

      rose.say = say;

      rose.say();

      getchar();

      return 0;

    }

 

此时Rose类的对象皆具有一项共同行为──说出其颜色。在软件中,靠say() 来表达这项行为。

    对于指令: rose.say(); 可解释为:将信息──say() 传送给Rose的对象,如﹕

其意义是﹕请问你是什么颜色呢﹖,兹以图示如下﹕

 

 

                                           say()信息

                price    month          ╭──────

              ╭────┬────╮   

                              │←─╯

    rose对象  ├────┴────┤

                    say()      

              ╰─────────╯

 

当此对象接到信息──say() 时,便启动其内含的say() 函数,并执行say() 函数内的指令。上述Rose的对象已具有一个函数──say() ,支持一项重要行为──Rose的对象能输出自己的内容。如果对其它行为有兴趣,可继续增加Rose类的函数,使其对象具有多样化的行为。

 

 

对象指针

   C所提供的指针可以指向基本数据型态(float)的变量。此外,指针也可以指向对象。例如上一小节的程序相当于﹕

 

#include "stdio.h"

#include "lw_oopc.h"

 

CLASS(Rose)

    {

        float price;

        int month;

void (*say)();

   };

 

static void say()

    { printf("price is RED\n"); }

 

void main()

    {

      Rose rose, *pr;

      rose.say = say;

      /*-----------------*/

      pr = &rose;

      pr->say();

      getchar();

      return 0;

    }

 

      因为对象是以C结构来实现的,指针可以指向结构变量,所以也能指向对象。此程序的pr正指向rose对象。 接下来,进一步看看say()函数如何存取对象的内容。例如:

  

#include "stdio.h"

#include "lw_oopc.h"

 

CLASS(Rose)

    {

        float price;

        int month;

     void (*say)(Rose*);

   };

 

static void say(Rose* p)

    { printf("%6.2f,  %d\n", p->price, p->month); }

 

void main()

    {

      Rose rose, *pr;

      rose.price = 38.25;

      rose.month = 12;

      rose.say = say;

      /* ----------------------- */

      rose.say(&rose);

      /* ------------------------*/

      pr = &rose;

      pr->say(pr);

      getchar();

      return 0;

    }

 

这个say()就输出rose的内容了。指令:rose.say(&rose)pr->say(pr)的意义是相同的。

 

构造器(Constructor)

      OOPC程序中,设计类是一件重要工作,其目的是﹕藉的产生对象。其中有个幕后工作者,它依照类的定义产生对象,可称的为对象的母。此幕后工作者就是构造器(Constructor) 函数。其主要功能为: 依照类的定义分配内存空间给所宣告的对象。在lw_oopc.h标头文件里已经定义了构造器宏,可以直接使用的。例如:

 

#include "stdio.h"

#include "lw_oopc.h"

 

CLASS(Rose)

    {

        float price;

        int month;

        void (*say)(Rose*);

   };

 

static void say(Rose* p)

    { printf("%6.2f,  %d\n", p->price, p->month); }

 

CTOR(Rose)

    FUNCTION_SETTING(say, say);

END_CTOR

 

void main()

    {

      Rose *pr = (Rose*)RoseNew();

      pr->price = 38.25;

      pr->month = 12;

      pr->say(pr);

      getchar();

      return 0;

    }

 

    CTOR就是Constructor字眼的简写,这个宏里定义了RoseNew()构造器,它会生成Rose的对象,并传回该对象的指针值,型态为void*。将的转为Rose*型态之后存入pr指针变量里。

 

类设计的实例说明

以电灯(Light)类为例

 

   基于前面所介绍的lw_oopc.h宏,就可以定义出类了,例如定义一个Light类,其light.h内容为:

 

/*  cx11-lig.h   */

#include "lw_oopc.h"

CLASS(Light) {        

    void (*turnOn)();

    void (*turnOff)();

  };

 

   类里的函数定义格式为:

     回传值的型态 (*函数名称)();

 

类定义好了,就开始编写函数的实现内容:

 

/*   cx11-lig.c   */

#include "stdio.h"

#include "cx11-lig.h"

 

static void turnOn()

   {  printf("Light is ON\n");  }

static void turnOff()

   {  printf("Light is OFF\n");  }

 

CTOR(Light)

   FUNCTION_SETTING(turnOn, turnOn)

   FUNCTION_SETTING(turnOff, turnOff)

END_CTOR

 

这个 FUNCTION_SETTING(turnOn, turnOn)宏的用意是:让类定义(.h文件)的函数名称能够与实现的函数名称不同。例如在light.c里可写为:

 

static void TurnLightOn()

{ .. }

 

CTOR(Light)

{

FUNCTION_SETTING(turnOn, TurnLightOn);

..

}

 

这是创造.c档案自由抽换的空间,这是实践接口的重要基础。最后看看如何编写主程序:

 

/*   cx11_ap1.c   */

#include "stdio.h"

#include "cx11-lig.h"

 

extern void* LightNew();

void main()

{   Light* light = (Light*)LightNew();

         light->turnOn();

         light->turnOff();

         getchar();

         return;

}

 

LightNew()是由CTOR宏所生成的类构造器(Constructor)。由于它是定义于别的档案,所以必需加上extern void* LightNew();指令。生成对象的基本格式为:

 

      类名称对象指针 = (类名称*)类名称New();

 

      示例:Light* light = (Light*)LightNew()

 

LightNew()构造器生成新对象,light是对象指针,它就指向此对象。 然后就透过light指标去调用成员函数了。

 

         下面介绍在上段内容中所提及到的#include "lw_oopc.h"

#ifndef LOOPC_H

#define LOOPC_H

#include

#define CLASS(type)\

typedef struct type type; \

struct type

 

#define CTOR(type) \

void* type##New() \

{ \

 struct type *t; \

 t = (struct type *)malloc(sizeof(struct type));

 

#define CTOR2(type, type2) \

void* type2##New() \

{ \

 struct type *t; \

 t = (struct type *)malloc(sizeof(struct type));

 

#define END_CTOR return (void*)t;  };

#define FUNCTION_SETTING(f1, f2)  t->f1 = f2;

#define IMPLEMENTS(type) struct type type

#define INTERFACE(type) struct type

#endif

         在上面所用的代码:

CLASS(Rose)

    {

        float price;

        int month;

     void (*say)(Rose*);

   };

 

         我们展开如下:

typedef struct Rose Rose;

struct Rose

{

        float price;

        int month;

        void (*say)(Rose*);

};

     经过CLASS宏,我们创建了一个Rose结构体,这个结构体包含成员变量pricemonth,以及一个函数指针,参数为一个Rose对象指针。

 

CTOR(Rose)

    FUNCTION_SETTING(say, say);

END_CTOR

我们展开如下:

         void* RoseNew()

{

        struct Rose *t;

        t = (struct Rose *)malloc(sizeof(struct Rose));

 

         t->say = say;

 

         return (void *)t; }

 

         经过这几个宏之后上述代码被展开成一个函数,这个函数创建了一个Rose对象,并且为这个Rose对象分配了内存空间,同时初始化了他的成员函数的函数指针指向。

阅读(2958) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~