兴趣是最好的学习契机!
全部博文(127)
分类: C/C++
2014-06-19 14:30:33
C语言的对象化模型
面向对象的特征主要包括:
.封装,隐藏内部实现
.继承,复用现有代码
.多态,改写对象行为
采用C语言实现的关键是如何运用C语言本身的特性来实现上述面向对象的特征。
1.1 封装
封装是一种信息隐蔽技术,它体现于类的说明,是对象的重要特性。封装使数据和加工该数据的
方法(函数)封装为一个整体,以实现独立性很强的模块,使得用户只能见到对象的外特性(对象
能接受哪些消息,具有那些处理能力),而对象的内特性(保存内部状态的私有数据和实现加工能
力的算法)对用户是隐蔽的。封装的目的在于把对象的设计者和对象者的使用分开,使用者不必
知晓行为实现的细节,只须用设计者提供的消息来访问该对象。
在C语言中,大多数函数的命名方式是动词+名词的形式,例如要获取一个semaphore,会命名
成take_semaphore,重点在take这个动作上。面向对象编程中刚好相反,命名为rt_sem_take,即名词+动词的形式,重点在名词上,体现了一个对象的方法。另外对于某些方法,仅局限在对象内部使用,它们将采用static修辞把作用范围局限在一个文件的内部。通过这样的方式,把一些不想让用户知道的信息屏蔽在封装里,用户只看到了外层的接口,从而形成了面向对象中的最基本的对象封装实现。
一般属于某个类的对象会有一个统一的创建,析构过程。
.对象内存数据块已经存在,需要对它进行初始化 – rt_sem_init;
.对象内存数据块还未分配,需要创建并初始化 – rt_sem_create。
可以这么认为,对象的创建(create)是以对象的初始化(init)为基础的,创建动作相比较而言多了个
内存分配的动作。
相对应的两类析构方式:
.由rt_sem_init初始化的semaphore对象 – rt_sem_detach;
.由rt_sem_create创建的semaphore对象 – rt_sem_delete.
1.2 继承
继承性是子类自动共享父类之间数据和方法的机制。它由类的派生功能体现。一个类直接继承其
它类的全部描述,同时可修改和扩充。继承具有传递性。继承分为单继承(一个子类只有一父类)
和多重继承(一个类有多个父类,当前RT-Thread的对象系统不能支持)。类的对象是各自封闭的,
如果没继承性机制,则类对象中数据、方法就会出现大量重复。继承不仅支持系统的可重用性,而
且还促进系统的可扩充性。
类似的实现代码如下程序清单:
/* 父类 */
struct parent_class
{
int a, b;
char *str;
};
/* 继承于父类的子类 */
struct child_class
{
struct parent class p;
int a, b;
};
/* 操作示例函数*/
void func()
{
struct child_class obj, *obj_ptr; /* 子类对象及指针 */
struct parent_class *parent_ptr; /* 父类指针 */
obj_ptr = &obj;
/* 取父指针 */
parent_ptr = (struct parent*) &obj;
/* 可通过转换过类型的父类指针访问相应的属性 */
parent ptr->a = 1;
parent ptr->b = 5;
/* 子类属性的操作 */
obj ptr->a = 10;
obj ptr->b = 100;
}
在上面代码中,注意child_class结构中第一个成员p,这种声明方式代表child_class类型的数据中
开始的位置包含一个parent_class类型的变量。在函数func中obj是一个child_class对象,正像这个
结构类型指示的,它前面的数据应该包含一个parent_class类型的数据。在第21行的强制类型赋值
中parent_ptr指向了obj变量的首地址,也就是obj变量中的p对象。好了,现在parent_ptr指向的是
一个真真实实的parent类型的结构,那么可以按照parent的方式访问其中的成员,当然也包括可以
使用和parent结构相关的函数来处理内部数据,因为一个正常的,正确的代码,它是不会越界访
问parent结构体以外的数据。
经过这基本的结构体层层相套包含,对象简单的继存关系就体现出来了:父对象放于数据块的最
前方,代码中可以通过强制类型转换获得父对象指针。
1.3 多态
对象根据所接收的消息而做出动作。同一消息为不同的对象接受时可产生完全不同的行动,这种
现象称为多态性。利用多态性用户可发送一个通用的信息,而将所有的实现细节都留给接受消息
的对象自行决定,如是,同一消息即可调用不同的方法。例如:抽象设备具备接口统一的读写接口。
串口是设备的一种,也应支持设备的读写。但串口的读写操作是串口所特有的,不应和其他设备
操作完全相同,例如操作串口的操作不应应用于SD卡设备中。
多态性的实现受到继承性的支持,利用类继承的层次关系,把具有通用功能的协议存放在类层次
中尽可能高的地方,而将实现这一功能的不同方法置于较低层次,这样,在这些低层次上生成的
对象就能给通用消息以不同的响应。
对象模型采用结构封装中使用指针的形式达到面向对象中多态的效果,例如:
/* 抽象父类 */
struct parent_class
{
int a;
/* 反映不同类别属性的方法 */
void (*vfunc)(struct parent_class *self, int a);
}
/* 抽象类的方法调用 */
void parent_class_vfunc(struct parent_class *self, int a)
{
assert(self != NULL);
assert(slef->vfunc != NULL);
/* 调用对象本身的虚拟函数 */
self->vfunc(self, a);
}
/* 继承自parent class的子类 */
struct child_class
{
struct parent_class parent;
int b;
};
/* 子类的构造函数 */
void child_class_init(struct child_class* self)
{
struct parent_class* parent;
/* 强制类型转换获得父类指针 */
parent = (struct parent_class*) self;
assert(parent != NULL);
/* 设置子类的虚拟函数 */
parent->vfunc = child_class_vfunc;
}
/* 子类的虚拟函数实现 */
static void child_class_vfunc(struct child class*self, int a)
{
self->b = a + 10;
}
以下是实现的例子:
基类里的虚函数参数void (*vfunc)(struct parent_class *self, int a); /包含一个基类类型指针.
下面每个派生类自己的虚函数实现void (*vfunc)(struct parent_class *self, int a); 也要包含一个基类的指针
struct parent_class
{
int a;
void (*vfunc)(struct parent_class *self, int a); //虚函数
};
struct child_class
{
struct parent_class base;
void (vfunc_my_implemention)(struct parent_class *self, int a);
};
void child_vfunc(struct parent_class *self, int info)
{
printf("这是子类实现的虚方法........\n");
}
void init_child_class(struct child_class *self)
{
((struct parent_class *)self)->vfunc = child_vfunc;
}
int main()
{
struct parent_class *ptrparent_class = NULL;
struct child_class c_object;
init_child_class(&c_object);
ptrparent_class = (struct parent_class *)&c_object;
ptrparent_class->vfunc(ptrparent_class, 2); //多态
return 0;
}