伯乐共勉

讨论。NET专区
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

小糊涂学symbian日记(7)

Posted on 2007-02-10 08:33  伯乐共勉  阅读(530)  评论(0编辑  收藏  举报
多重继承的使用有如下的接口使用的限制,C++的多重继承机制并不复杂。 This is perhaps recognised by the OO community now.例如Java就只允许单个继承, but the interface and extends keywords support the same facilities as are provided by M classes. 下面展现了更多的限制细节: 

首先,M classes主要是用来定义协议的,而不是用来完成它的。特别要指出的是,它们不具备任何成员数据。这个限制导致某些特定类型的行为(譬如说是活动对象)在接口中不能被很好的封装,但是必须按照传统的方法来继承。 

其 次,一个c类可能从另一个c类中派生,或者是多个M类,这个限制可能导致这样一个事实,那就是多重继承只能被使用在接口中。 It implies that it is still possible to uniquely identify a primary inheritance tree (the C class hierarchy), with interfaces as a side feature.如果允许任意的多重继承,那 将不可能再找出一个主继承树,这个限制也证明了C类将不可能成为一个multiple base class, which makes it unnecessary to consider the complications of multiple base class inclusion, virtual inheritance, etc. 

第 三,C类必须是在基类列表中首要的特殊类,This emphasises the primary inheritance tree and,  importantly, it makes convnsions between any C class(including those with interface)and void* pointers freely possible.Admittedly, the C++ standards do not mandate that object layout follows the order in which base classes are specified, but in practice this is the case for most compilers, including those used for the Symbian platform. 

第 四,no M class may be mixed in more than once in any class,  either as a direct base or as a base of any of its primary base classes. To put it another way: when deriving a C class CD from a base class CB, you may not mix in anyMclass MP which has already been mixed into the derivation of CB. This reflects the fact that CB already supports the protocol defined by MP: there is nothing to gain from mixing in this protocol class again. In addition, it makes it unnecessary to consider the complications of multiple base class inclusion, virtual inheritance, etc. 

最 后,although it is legal to derive one M class from another,  it is not legal to include a protocol twice by including both it and a derived protocol into a C class, at any point in the C class’s base class graph. To put it another way, if there is a class MD derived from MB, then a C class cannot include both MB and MD. This is because any function in the C class which provided an implementation of MBprotocol could conflict with the implementation of MDprotocol. 


[Descriptors] 
Overview 
--------- 
Descriptors are both fundamental to the Symbian platform, and an excellent example of the difference of approach between non-OO and OO designs. 

在C中,一个字符串看起来时如下定义的: 
char* hello = "hello"; 

导致产生一块包含六个字节(‘h’, ‘e’, ‘l’, ‘l’, ‘o’, ‘’)的内存。 

Abstract descriptors 
--------------------- 
我们可以用两个类来满足字符串的基本需求:TDesC和TDes。 

TDesC 时一个常量般,或者时不可修改的描述符。它有一个地址和长度。Using a class like this,  you can do any manipulations to a string, provided they do not alter the data. As a consequence, the TDesC class has many non-modifying functions. 

TDes 是一个可以修改的描述符:it has, in addition, a maximum length.这就允许数据被扩展或压缩,只要不超过最大的长 度,可以任意的处理。As a consequence,  TDes has many modifying functions which allow string manipulation. 

描述符有个基本的特性那就是他们不允许超过已分配长度的字符串操作。其他类可能支持这点,如CBufBase和起派生类。但如果TDes溢出了,那就可能发生不可预料的错误。 

Pointer descriptor 
------------------- 
抽象的描述符有几种,其中最简单的就是指针描述符。 
TPtrc只有长度和地址:它只需要两个机器字。一个TPtr多了一个最大长度,and so may be used to describe a buffer which is perhaps only partially allocated。 

TPtrC和TPtr有点象C中的char*指针,但是由于长度已经包含在描述符中了,所有没有必要包含一个''空白字符。 

Buffer descriptors 
-------------------- 
Buffer descriptors,TBufC和TBuf包含数据在自身,就好象C中的char[] 
同样TBuf包含一个最大长度,如TBuf<12>这样最大长度就是12。 
这两种描述符使用了C++的模板机制,使用了一个整形的参数指明长度,如上:) 

Heap descriptor 
---------------- 
堆描述符,HBufC将数据存放在一个堆单元。 

这 有点象C语言中的(char*) malloc(length+1),同样,这也是用在你不得以知道最终需要缓冲区大小的情况。因为 buffer descriptors是分配在堆上的,所有他们总是通过HBufC*来使用,好过直接使用HBufC(这样避免直接使用堆地址)。 

Descriptor classes 
--------------------- 
描述符使得描述字符数据变得很容易。当你要堆字符串使用描述符时,记得应该总时使用TDes等类,如果时要构建一个unicode版本,那就使用TDes16等。 
如果你要描述字节数据,那最好使用TDes8等。 

[Thin templates] 
C++模板机制允许你去编译一个带参数的类,这个对于集合类来说很有用的,如数组,类的描述为: 
class CArrayFixFlat ... 
指明一个数组类的族群,可以包含任何类型。模板是可以被实例化的,如: 
CArrayFixFlat* iControls; 

这里描述了一个CCoeControl*的指针数组,其他集合类使用同样的方法,如TPckgBuf, TSglQue等。 

模板也可以使用其他参数,如一个整数: 
class TBufC ...; 

那这个模板可以被实例化为TBufC<20> name; 

用模板来实现的函数是很强大的,如果没有模板,那集合类就可能要使用void *指针,作为结果,这可是类型不安全的。 

Templates can be difficult to manage. A template really defines a whole family of classes. Each member of that family that is instantiated requires its own object code. Avoidance of object code duplication is a difficult issue in a C++ implementation. For the Symbian platform, object code duplication must be avoided at all costs — the solution used is the thin template idiom. 

[Code efficiency] 
Stack usage 
------------ 
每个线程都有一个8KB标准大小的堆栈,我们得小心管理它,因此: 
1、避免直接的值拷贝,除了基本类型:) 
2、在堆中生成大的对象或数组,而不要在栈中生成。 
3、适当的定义变量以使他们的生命周期为最短。 

上面第三点可以通过下面的例子来说明 
void ABadFunction() 
{ 
  TBigObject Object1; 
  TBigObject Object2; 
  TBigObject Object3; 
  GetTwoObjectvalues(Object1, Object2); 
  Object3 = SumObjects(Object1, Object2); 
  FunctionWithUnknownStackOverhead(Object3); 
} 

在上面的代码中,Object1和Object2的生命周期一直持续到FunctionWithUnknowStackOverhead()的执行,而事实上他们在这里是不必要的,他们应该在这个函数调用前就释放掉,我们可以如下来实现: 
void ABetterFUnction() 
{ 
  TBigObject Object1; 
  GetTotalObjectvalues(Object1); 
  FunctionWithUnknowStackOverhead(Object1); 
} 

void GetTotalObjectvalues(TBigObject &aObject) 
{ 
TBigObject Object1; 
TBigObject Object2; 

GetTwoObjectvalues(Object1,Object2); 
aObject=SumObjects(Object1,Object2); 
} 

将代码分在两个函数中,你可以得到无浪费的stack。 

Function overloads 
-------------------- 
如果函数拥有缺省的参数,而且如果调用者可能经常使用缺省参数来调用,那就建议重载这个函数,这是因为编译器每次都要提供一个缺省的参数,那在函数调用时就会造成多余的代码产生。 

For example, if you have 
void FunctionOne(TInt aInt=0); 

而在代码中经常这样调用它 
FunctionOne(); 

那建议定义一个 
void FunctionOne(); 
它可以这样处理 
void FunctionOne() 
{ 
  FunctionOne(0); 
} 

真不愧时有效率的symbian连c中的缺省函数可能造成的累赘都抛弃了,一切轻装上阵了:) 


Pointers and references 
------------------------- 
使用引用作为函数的参数可能比使用指针更有效率。This is because the compiler has to preserve the value of the null pointer through all conversions. 

总之是函数参数的传递,我们使用引用更加能使代码有效。 

Floating point maths 
-------------------- 
浮点数运算很耗时,如果可以,我们尽量使用整数。 
For example, given two TInts, aTop, and aBottom, instead of: 

TReal a = (TReal)aTop; 
TReal b = (TReal)aBottom; 
TReal c = a/b+0.5; 
TReal result; 
Math::Round(result,c,0); 
return (TInt)result; 
you should use 

return((2*aTop+aBottom)/(2*aBottom)); 

Inline functions 
------------------- 
内联函数是为了提高代码执行速度,避免函数调用的开销而设计的,而且可以保留(虚假的)函数模块性。在使用他们之间,有两个地方你需要留意: 

1、代码的紧凑性:内存资源的限制意味着牺牲速度的函数比大体积的内联代码更好。 
2、二进制兼容性:一个内联函数的修改可能导致兼容性的更改,This is important if your code is going to be used by other developers. 

下面是大多数应该使用内联函数的情况: 
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; }; 
3、in the thin-template idiom 
4、 certain other operators and functions, possibly templated, whose definition, not subject to change, is to map one operation onto another, for example, 

template inline T Min(T aLeft,T aRight) 
{ return(aLeft
No test for NULL pointer when deleting object 
------------------------------------------------- 
C++指定deltete 0不做任何事情,所以别写出这样的代码: 
if (iX) 
delete iX; 

真是好symbian,不要求出现一点冗余的代码,这里的if当然不可不必出现:) 

[Defensive programming] 

Overview 
--------- 
为了帮助开发者在开发过程中更容易的发现潜在问题,symbian提供了宏来测试函数和类中的error conditions。 

Casting is one well known source of hard-to-find errors. 

Testing conditions with asserts and invarians 
----------------------------------------------- 
较早的捕捉到错误的一个方法就是在函数开头和结尾来判断一个条件是真还是假,and raise errors if they are not. 
有两种用来支持这种编程风格的机制: 
1、asserts 
2、class invariants 

——Asserts 
  有两个宏用来asserting specific conditions in functions: 
  __ASSERT_ALWAYS用来捕捉运行时的非法输入,release和debug编译模式下都可以执行。 
  __ASSERT_DEBUG用来捕捉编程错误,只能用在debug编译模式下。 

——Class Invariants 
  Class invariant are used to test that an object is in a valid state.They are used only in debug builds. 
  1、为重要的类使用__DECLARE_TEST来定义class invariants,The class must supply functions that specify its allowed stable states. 
  2、要在执行函数之前确定函数是否处于稳定状态,call the invariant at the start of all public functions using __TEST_INVARIANT. 
3、For non-const functions,  you can ensure that the object has been left in a stable state by also calling the invariant at the end of the function. 

——Casting 
  Casts, 在其他操作系统中,使用时将没有警告。If a cast seems to be needed, check that this does not reflect a design weakness. 

  Symbian平台提供它自己的宏来封装C++ cast操作: 
  1、REINTERPRET_CAST 
  2、STATIC_CAST 
  3、CONST_CAST 
  4、MUTABLE_CAST 

  注意,从6.0版本之后,开发者利用原始的C++ casting operators要好过使用这些宏,事实上,这些宏时为早期支持cast而做的。 

  更加复杂的dynamic_cast将不会在EPOC中出现,因为它根本不支持运行时类型识别(RTTI),这就说明symbian的对象动态生成以及永久存储等都有它一套自己的实现基础机制。 

[Static date] 
Writeable static data in DLLs 
--------------------------------- 
Symbian 平台时专为ROM-based computing而设计的。DLL时长驻ROM并且不能被写入。 Although DLLs may in some cases be RAM loaded, the Symbian platform makes the conservative assumption that no DLL can be written to.  因此在symbian平台中DLL时没有数据段的,从而我们得到一个结论那就是没有DLL能包含可写的静态数据,不管有没有初始化。 

静态数据是任何在一个函数之外所描述的数据 

TBufC<20> fileName; 
void SetFileName() 
{ 
... 
} 
大 部分程序都要大量使用这种数据,在模拟器上,以上的运行不会引起什么不适,这是因为有windows的DLL机制,在它的下面,数据是可以被写入的。但这 样的代码将不适合在非模拟器上运行。编译将会跟随着一个petran tool的警告而出错,这是在非模拟器目标编译的最后一个环节。产生的消息可能如 下: 
ERROR: DLL 'MENUITEMS[10008ADO].APP' has uninitialised data. 

你必须注意不能有writeable static在你的DLL中,以避免这样的错误。 

因 为在某些情况下,在DLL中保存数据是很有用的,因此symbian平台也提供了一个机制来允许DLL在单个线程的基础提供私有数据的存储管理,比如线程 局部存储。A per- thread mechanism is chosen to avoid potential ambiguities which otherwise arise over which of potentially many users of the DLL should see the data. 

Porting strategies 
-------------------- 
完全避免可写的静态数据也许是个很艰巨的任务,在一般情况下,你需要将所有的静态数据都放在一个结构中,然后通过一个指针访问这个结构。 

下面有几种方法可以使你的指针有效: 
1、把它做为函数的参数传递到需要它的地方。这种方法可能包含大量的代码转换——不光使调用这些可写静态数据的函数,而且包括那些间接调用这些函数的的函数。 

2、使用DLL线程局部存储,这也会引起问题,因为这样的DLL函数要比一个指针引用复杂许多。 

3、把指针存储在各种结构和类中,以便需要他们的函数能有效的使用他们,这也会引起代码的改动,但可以避免间接函数调用的缺点和线程局部存储的缺点。 


实际上,经常使用上述三种方法的混合物,以达到代码和执行效率的平衡。 

Desing Strategies 
-------------------- 
新 的代码要考虑到避免可写静态数据,在面向对象系统中, it is not considered unusual to pass pointers to global data along with object references, and the this pointer available in every non-static member function gives access to all members of that object. 

只要适当的运用,大部分使用静态可写数据的情况使可以避免的。 

Non-obvions use of wirteable static data 
------------------------------------------- 
有时候,静态可写数据会出现在一些暗处,例如: 
const TRgb9(255, 255, 255) KRgbWhite; 

定义了一个在symbian代码中不会被写入的常量,不管怎么说,当TRgb使一个类时,它的构造函数会被调用,为了构造出这个常量,因此这个常量还是可写入的。 

最好写成这样: 
#define KRgbWhite TRgb(255, 255, 255) 

This will cause the class constructor to be invoked at the point of use, rather than when the DLL is loaded. 

Writeable static data in EXEs 
---------------------------------- 
在exe文件中,是没有静态可写数据的限制的,如做为server的exe就可以使用静态可写数据。