[深入分析BREW机制]:BREW的类机制

 BREW的类机制
 ——目录——
 引言
 1、BREW类声明透析
 2、BREW类实现透析
 3、BREW类的创建过程
 结语

引言
 Brew开发包由两部分组成:标准接口、助手函数。我们可以利用brew sdk开发第三方应用,也可以开发新的接口,第三方接口称为扩展接口(extension interface). 所有的标准接口、扩展接口以及brew应用的实现机制都是一样的,我称之为Brew类机制,所有的标准接口、扩展接口以及brew应用都可以看作是一个Brew类。
 一个brew类通常包含两部分:1)供外部使用的部分,称为类声明,放在.h文件里;2)类实现部分,放在.c文件里。下面对brew类的实现和动作机制加以分析:

1、BREW类声明透析
1)在.h文件里定义了两个结构体,一个我称之为类声明结构体(用A表示),另一个为虚函数表结构体(用vtblA表示)。在虚函数表里的每一个函数在.h文件里都有一个配对的宏,宏的目的是简化外部程序对虚函数表里的函数的调用。三者关系如示例代码所示:
//假设A是你要创建的接口的名子
typededef struct _A
{
 struct _vtblA *pvt;
}A;
typedef struct _vtblA
{
 //virtural function table.
 //每一个成员,即是一个函数指针
 ... (*function1)(A* pi, ... ...);
 ... (*function2)(A* pi, ... ...);
}vtblA;

#define FUNCTION1(pi, ... ...) ((A*)pi)->pvt->function1(pi, ... ...)
#define FUNCTION2(pi, ... ...) ((A*)pi)->pvt->function2(pi, ... ...)

2)pi是接口实例的指针(实例的概念马上就要讲到),会被强转为A类型(为什么能这么做?请读者看完全文后思考), 然后A类型的pi就成为访问虚函数表里的函数的入口了。只要对brew标准接口有些了解,就会发现所有接口的接口函数的第一个参数都是该接口本身的指针,这个指针就是宏里的pi,而接口函数就是这些宏(在该接口的.h文件里可以查找到这些宏),也正因此,除了IShell接口外,所有接口在使用前都要先创建实例(读到这里,大家想一下“brew接口函数的第一个参数是接口本身的指针”这句话的对错,也请与c++方法调用的方式有什么不同)。
3)A类型的结构体只有一个成员,是一个虚函数表指针,这个指针指向一个vtblA类型的结构体(赋值操作在.c文件里进行,对外部不可见),vtblA的每一个成员都是一个函数指针,在调用这个指针时触发的是哪一个实现函数由.c文件来决定。所以对于一个brew接口,发布后,它的.h文件内容是不准修改的,而.c里的函数实现却可以不停地修改,甚至可以让函数指针指向另一个实现函数(多态)。
函数指针的概念请另查阅相关资料。

2、BREW类实现透析
1)在.c文件里定义了一个结构体、一个全局变量、和一组实现函数。
这个结构体,我称之为类实例结构体,这个全局变量的类型是vtblA, 是这个类的真正的虚函数表,它的成员的值就是这组实现函数。三者关系如示例代码所示:
//实现函数声明
... Function1(A* pi, ... ...);
... Function2(A* pi, ... ...);

//类实例结构体类型
typedef struct _CA
{
//A类型的成员a
A a;
//其它数据成员
... m_nData1;
... m_nData2;
}CA;

//虚函数表全局变量
const vtblA g_vtblA =
{
 Function1;
 Function2;
};

2)看上面的示例代码,注意到类实例结构体的第一个成员是该类的类声明结构体,这是一个约定,所有的Brew类在设计时都必须遵循。为什么这样?这是brew类封装机制的基础,再看示例代码,.h文件中所有函数指针的第一个参数都是类声明结构体指针,与此对应,.c文件里所有实现函数的第一个参数也是类声明结构体指针,其实,这个指针实际是指向一个类实例结构体(CA),由于CA中的数据成员是私有的,所以对外一般将CA指针强转为A指针,实现CA数据成员的封装,而在类实现函数内部又会将A指针强转回CA指针,以便访问类的数据成员。是不是很聪明,这样一来,外部调用者还以为A结构体就是这个类的全部,实际在A结构体的后面还跟了一溜重要的数据成员(理解C语言结构体成员的位置相对固定的特性很重要)。

3、brew类的创建过程
 先看c++类,在使用一个c++类时,第一步是new一个该类的对象,在brew类中,与c++类对象等同的概念是类实例,在使用一个brew类提供的接口函数时,第一步是创建该类的类实例,创建方法是调用ISHELL_CreateInstance(),下面的示例代码创建类A的实例:
A* pA = NULL;
IShell * pIShell = AEE_GetShell();
IShell_createinstance(pIShell, AEECLSID_A, &pA);
这一步等同于c++类对象的new操作。
AEECLSID_A是类A的唯一标志,由qualcomm分配,在上线之前,可以指定一个临时的,只要不与自已的其它类的clsid相同即可。IShell_createinstance通过AEECLSID_A与类实例建立联系(如何建立关联?请读者思考,也可在idle兵团的其它文章里找到答案),然后分配一块sizeof(CA)大小的内存,用于装载类实例结构体,并做初始化,最后输出该内存的地址,注意,输出类型不是CA,而是A,输出时做了强转,如第三者2)节所述。刚才提到了初始化,其中初始化的最重要一步就是将虚函数表全局变量g_vtblA的地址赋给结构体的虚函数表指针,如下:
(A *)pA->pvt = &g_vtblA;
这一步完成了从虚函数表里的函数指针到实现函数的关联操作。

结语
在理解brew类时,要善于借用c++类的一些思想,因为两者很相像,brew类具有很好的封装性、继承性和多态性,甚至比c++类更灵活,本质的区别在于brew类是基于com思想,用纯c构建而成的。

welcome to my blog: blog.csdn.net/chinesecoolman

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值