关闭

BREW接口的继承和多态

1338人阅读 评论(1) 收藏 举报

BREW接口的继承和多态

李振鹏

版权所有,转载请注明出处          

        BREW 平台支持 C C++ 开发语言,而 C 语言是不支持面向对象的,只有 C++ 支持面向对象的程序设计,因此 BREW 中必须使用 C 语言模拟实现 C++ 语言面向对象机制。在 C++ 中继承是基于 C++ 内存中数据是按照一定顺序排列的,而 C++ 的多态是基于虚函数表机制的。通过具体对象实现替换虚函数表中某些函数项的位置实现多态。

       C 结构体中内存对象的布局是采用按照定义顺序排列的,也就是采用如下的方式:

       如果结构体定义如下:

struct A

{

       int a;

       int b;

       int b;

};

       在内存中的各个程序的布局如图所示:

int a;

int b;

int c

                             图 1 内存中数据的排列

       而如果结构体的定义如下:

struct B

{

int a;

int b;

int c;

int d;

};

       在内存中的布局如图所示:

int a;

int b;

int c;

int d

                          图 2  增加成员后内存中数据的排列

       这样可以看出完全是按照定义的顺序布局的,而如果采用如下的方式定义结构体 C

struct C

{

       struct A a;

       int d;

};

       其在内存中的布局如下图所示:

struct a

int d

                          图 3  含有结构体成员的内存布局

       这样结构体成员 a 在内存中的布局如图 1 所示,如果把 a 全部展开,就与结构体 B 的内存布局图 2 相同,这样如果存取结构体 C 中的 a 的成员 a ,虽然采用 C.a.a 才可以,但是内存中的布局与直接访问 A.a 效果是相同的,只不过增加了一个编译中间层,因此,在 BREW 中可以依照此技术实现继承机制。而把父结构体当作子结构体的第一个成员变量时,可以在运行时通过使用父结构的成员访问方式直接访问实际子结构体的成员变量,并且可以使用类型转换,转换为实际的子结构体,这样的技术正好是面向对象程序设计中继承和多态技术的基础。

       而面向对象技术中的关键技术多态是基于以上的内存模型和函数指针实现的,一般来说,如果使用类 C 语言描述多态,它相当于增加了一个间接层,在这个间接层拦截对于方法的调用,然后根据具体的指针指向实际对象调用相应的方法实现。必须完成如下的步骤:

1 )获知方法调用的全部信息,包括调用的是哪个方法,传入的实际参数有哪些;

2 )获知调用时指针所指向的实际对象;

3 )根据( 1 ),( 2 )所获得的信息,找到合适的实现代码,执行调用。

       从上面的步骤可以看出,应该根据怎样进行信息进行方法的查找,一般来说,有两种思路:

       一种是根据方法的名称进行查找,通过比较字符串的方式进行查找,因此效率较差,但是由于字符串具有唯一性,因此不必为了对齐,而浪费空间,占用空间小;

       另一种方法是基于绝对位置的定位技术,其查找结构十分的简单,只由一个存有方法地址的指针数组,也就是通常说的虚函数表,这种方法速度快,只需要进行一次简单的地址相加就可以了,但缺点是不管派生类有多少项需要更改的基类虚函数,都必须拥有和基类相同大小的虚函数表,因此浪费了空间,这种方式运行速度极快,并且是 C++ 语言所采用的技术,因此基于速度考虑,同时为了兼容 C/C++ 语言开发, BREW 采用这种模型。

       在接口中定义大量的函数指针,并且 BREW 规定所有 BREW 的模块必须实现 IBase 接口,而 IBase 接口的实质为如下的定义:

struct IBase

{

       uint32  (*AddRef) (iname*);

       uint32  (*Release) (iname*);

};

       为了能够顺利从 IBase 接口派生特别定义了宏 INHERIT_IBase ,它其实就是如上的接口定义,而且 BREW 的定义模型,为了防止在接口之间转换造成混乱,只支持单根继承,也就是说用户每次只能够从一个接口派生,并不能同时派生多个接口,派生时使用如下的方式:

struct IInherit

{

       INHERIT_IBase(IInherit);

       void (*func1)();

       void (*func2)();

};

       这样根据上文介绍的内存模型,可以使用 IBase 接口来访问他的成员,而实际上访问的是 IInherit 接口的成员。

       但是这样只是定义了接口,并没有具体的实现,可以再另外定义新的结构体,以此接口的成员函数指针作为结构体的第一个成员变量,这种内存布局方式,与 C++ 中单继承的虚函数表的内存布局方式相同,并且这第一个指向接口的成员就是虚函数表。具体定义如下所示:

struct IInherit_class

{

struct IInherit *pvt;

int a;

int b;

};

       这样就可以根据上文介绍的内存映射技术,使用 IInherit 的接口访问 IInherit_class 的成员,而且只通过 IInherit 接口并无法访问实际的数据成员,这样就达到了数据封装的效果。

       但是对于这个所谓的虚函数表,与 C++ 中不同的是必须由用户进行初始化,编译器并不会初始化,因此要给接口成员分配内存,并且使函数指针指向实际的函数地址。并且根据不同的实现类指向不同的实现函数,这样运行时就可以只根据接口访问调用实际指向的对象的方法,从而实现运行时的多态。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:32192次
    • 积分:654
    • 等级:
    • 排名:千里之外
    • 原创:15篇
    • 转载:0篇
    • 译文:0篇
    • 评论:160条
    文章分类
    最新评论