Symbian OS 使用的是面向对象的C++, 但是又和标准的C++有一些区别。 比如Symbian OS没有标准的异常处理(Exception), 因为设计Symbian OS的时候还C++还没有把异常处理标准化。 所以Symbian设计了自己得异常处理机制: TRAP, leave。 另外就是今天要讲的基本类型。 Symbian 基本上不使用任何标准的C++基本类型, 众所周知,不同的C++编辑器对int, unsigned int的长度理解不同, 所以Symbian OS中使用 TInt8,TInt16,TInt32。
但是,如果你么没有很好的理由使用某一特定的长度时,应该使用Tint。 其他很多类型也遵守这个原则。 比如以后要讲的TBuf8, TBuf16可最好用为TBuf。
类 型 | 描 述 |
TInt8, TUint8 | 8 位 整数 |
TInt16, TUint16 | 16 位 整数 |
TInt32, TUint32 | 32位 整数 |
TInt, TUint | (32位)整数 |
TReal32, TReal64 | 实数 |
TText8, TText16 | 字符, 相当于 unsigned char, unsigned short int |
TBool | 布尔 |
TAny | 相当于void |
代码规范
Symbian OS 使用很多代码规范, 使用他们可以增强Symbain 代码的可读性, 有些规范甚至是需要严格遵守的, 比如类的命名:
种 类 | 例 子 | 描 述 |
T classes | TDesC, TPoint | 这个类可以向基本类型一样使用,因为他们通常很小,而且不使用heap所以也没有析构函数 |
C classes | CConsoleBase, CActive | 这个类是Symbian使用最多的类,C代表他们从CBase类继承而来, 他们必须有析构函数因为他们的对象创建在heap中 |
R classes | RFile, RTimer | R代表资源(Resource),它们只是一个系统资源的句柄,他们本身被创建在Stack上, 但是他们所使用的资源被创建在heap上,使用完毕需要Close() |
M classes | MEikMenuObserver | 这个类是一个空的接口,使用的时候需要从它继承 |
static classes | User, Math | 这个类只有静态函数, 一般都是库函数 |
Structs | SEikControlInfo | c - struct |
变量命名
种 类 | 例 子 | 描 述 |
枚举 | EMonday,ETuesday | E 代表枚举 |
定量 | KMaxFileName | K 代表定量 |
成员变量 | iDevice, iX | i 代表成员变量 |
参数 | aDevice, aX | a 代表参数 |
局部变量 | device, x | 局部变量没有固定的规范 |
1、T类型
*大多数T类型都足够简单而不需要什么构造函数,如果要,那它的功用也就是初始化成员数据。
*一个拷贝构造函数或是赋值操作都是很少见的,这是因为T类型的拷贝很简单,一般只是成员之间的互相拷贝,而这个正是编译器自动生成的构造函数和赋值操 作所完成的功能。当然这些函数有时候是必须的——如果T class是一个模板类,而其模板参数是个整数.如此的话,在一个TX<32>和 TX<40>之间拷贝或赋值就需要费更多的周折,那就需要好好的对一个拷贝或赋值操作来进行明确编码了。
*T类型没有析构函数,因为没有外部资源要被清除。
*T类型可以在堆栈中被很安全的释放,不需要调用什么析构函数,因为它没有外部资源要去额外处理。
*T类型在作为函数参数时可以通过值或引用来传递。
*所有的内建类型都符合T类型的标准,因此也有T前缀,如TInt
2、C类
大部分类都是C类,它们直接或间接从CBase类派生。
从CBase类派生的类拥有如下的特性:
*它们都是在堆上分配的,并且不应该是其他类的成员。
*在C类对象的分配中,所有的数据成员都被初始化为binary zero.
*它们通过指针或引用被传递,除非有明确意图,否则并不需要一个明确的拷贝构造函数或赋值操作。
*它们都拥有很特别的构造函数,因为异常随时可能发生,所以它们都用上了双重构造,一般的C++构造函数不适合有异常出现的情况,但是ConstructL()函数就可以处理异常发生的情况了。
*他们有一个虚析构函数,用来做标准的清除工作。
3、R类
R类对象拥有如下的特征:
*真正的对象是由另洋线程或地址空间的server所拥有的
*真正的对象的处理是对client不见的,既无关的。
下面是其关键特征:
*如果一个R对象打开后就必须被关闭(用open和close函数)。一般来说,如果负责打开对象的那个线程被中断了,那和该对象联系在一起的资源就被自动关闭了。
*他们没有明确的构造、析构或拷贝构造函数以及赋值操作。
*他们没有一个统一的基类。
*初始化函数可能有不同的名字,如Open()、Create()或Allocate()等。
*终止函数也有不同的名字,如Close()、Destroy()或Free()等
*当R类拥有外部资源时,那就有了清除的需要,这个处理是因地制宜的:)
4、M类
M类定义了抽象的协议或接口,其具体的处理由派生类来完成
M类拥有以下约束
*它们不应该包括任何成员数据
*它们不应该包括析构或构造函数,以及对=的操作重载。
M类通常包括一系列定义抽象接口的纯虚函数,有些M类可能提供成员函数的处理(尽管有上面的约束)
M类是symbian平台上唯一使用多重继承的类。
[C++和机器架构]
1、算术类型
------
在大多数机器里,int是位字节,老的机器里可能是16位,新的机器则可能达到64位。
在symbianOS中,TInt和TUint被定义为内建的int和无符号int类型,并且至少是达到32bits。
当你需要具体尺寸时,可以使用下列几种类型:
*TInt32/ TUint32
32位signed 和 unsigned 整型。
*TInt8 /TUint8 /TText8
8-bit的signed和unsigned整型,以及8-bit character
*TInt16 /TUint16 /TText16
16-bit signed和unsigned整型,以及16-bit character
*TTint64
64-bit unsigned integer
当ARM没有支持内建的64-bit运算时,TInt64是使用C++类的完成。
*TReal/ TReal64
双倍精度的浮点数,这个是推荐使用的一般浮点数类型,ARM架构并没有提供浮点的支持,你应该尽量使用整形运算(例如,大多数GUI计算),只有当程序真的需要时(如电子表格程序)才不得不使用这些浮点数类型。
*TReal32
32-bit浮点数,这个更小也更快,不过精度不是很另人满意的。
2、复合类型
--------
*
struct TEg
{
TInt iTnt; //offset 0, 4 bytes
TText8 iText; //offset 4, 1 byte
//3 wasted bytes
TReal iReal; //offset 8, 8 bytes
} //total length = 16 bytes
//都是以4 bytes = 32 bits为一个单位的
*通常我们需要一个指向包含任何可能内容的内存的指针,在C++中我们通常用void*指针来表示,但在symbian平台中,我们用TAny* 来代替它。
*在symbian中处理字符串的方法是descriptors。
*参数中传递过多的数据是不明智的,事实上,任何超过2个机器字的数据都是不提倡的,因为这将引起过多的拷贝动作,相反用一个指针或引用来处理这些数据的地址要比传递数据本身好得多。
*
foo(CContainer* aContainer)
{
TEg s;
TEg* ps=&s;
aContainer->iMember=ps;
}
这个地方有个严重错误,就是返回了函数的局部变量,注意比较隐含
*
symbian平台中堆栈上的对象的生命周期和标准C++中的非常相似,不过对其的控制有不同,如下:
void Fool()
{
CS* s = new (ELeave) CS; //allocate and check
CleanupStack::PushL(s); //push, just in case
s->ConstructL(p1, p2); //finish constructing - might leave
s->UseL(p3, p4); //use - might leave
CleanupStack::PopAndDestroy(); //destruct, de-allocate
}
这里表现出四件大事情:
(1)所有的堆分配类都是C开头的,它们都是从CBase继承来的。
(2)使用了清除栈,以在异常发生后对象能被及时清除。
(3)任何可能引起异常的函数都有一个L后缀。
(4)使用new(ELeave)以防止分配空间时出现异常。
3、Descriptors
----------
*我们可以用两个类来满足字符串的基本要求:TDesC和TDes。
TDesC是一个常量、不可更改的描述符,它有一个地址和长度。我们可以象类一样使用它,但是不能对字符串做任何修改。
TDes是一个可以修改的描述符,和TDes比起来,它还有个最大长度,只要不超过最大的长度就可以对字符串做任意的处理。
*描述符有个基本的特性,那就是它们不允许进行超过分配长度的字符串操作,其他类可能支持这点,如CBufBase及其派生类,因为如果TDes溢出了,就可能发生不可预料的错误。
*描述符分几种:
(1)指针描述符
TPtrC只有长度和地址,因此它只需要两个机器字。
TPtr多了一个最大的长度。
TPtrC和TPtr有点象c中的char*指针,但由于长度已经包含在里面了,所有不需要结尾空字符。
(2)缓冲描述符
TBufC和TBuf包含数据在自身,就好象C中的char[]
同样TBuf包含着一个最大长度,如TBuf<12>就是最大长度为12
这两种描述符使用了C++的模板机制,使用了一个整形的参数指明了长度。
*堆描述符
HBufC将数据存放在一个堆单元中。
这有点象C语言的(char*)malloc(length+1),同样这也是在你不得知道最终需要缓冲区大小的情况。因为缓冲描述符总是分配在堆上的,所以它们总是通过HBufC*来使用的,好过直接使用HBufC(这样避免直接使用堆地址)
*类描述符
如果你要描述字符数据那就使用TDes等类,如果要构建一个unicode版本,那就使用TDes16等,如果要描述字节数据,那就使用TDes8等。
(参考图)
[堆栈使用]
每个线程都有一个8KB大小的堆栈,我们得小心的管理它:
1、避免直接的值拷贝,除了基本类型
2、在堆上生成大的对象或数组,而不要在栈上生成
3、适当的定义变量以使他们的生命周期为最短
[函数重载]
如果函数拥有缺省的参数,而且如果调用者可能经常使用缺省参数来调用,那就建议重载这个函数,这是因为编译器每次都要提供一个缺省的参数,那在函数调用时就会造成多余的代码产生。
For example, if you have
void FunctionOne(TInt aInt=0);
而在代码中经常这样调用它
FunctionOne();
那建议定义一个
void FunctionOne();
它可以这样处理
void FunctionOne()
{
FunctionOne(0);
}
[内联函数]
在以下情况使用最好:
1、getter and setters for one- or two-machine word quantities: for example, inline ConEnv() const { return iConEnv; };
2、trivial constructors for T classes:
inline TPoint::TPoint(TInt aX, TInt aY) { iX=aX; iY=aY; };
[Assert]
有两个宏用来在函数中进行断言:
__ASSERT_ALWAYS用来捕捉运行时的非法输入,release和debug编译模式下都可以执行。
__ASSERT_DEBUG用来捕捉编程错误,只能用在debug编译模式下。
另外,如果你想查看Symbian的类图的话你可以这里找到:开发工具的帮助文档里的index下的 C++ Class Hierarchy.例如在CodeWarrior中也有!