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