在Symbian中,很多C++基本类型都被重新定义了,最好使用Symbian的,理由如下:
typedef signed int TInt; C++中的signed int,32位,基本用法类似。
typedef unsigned int TUint; 一般用于计数器(Counter)或者标记(Flags)。
其他Int类型:TInt64, TInt32, TInt16,TInt8; 同时有一份TUint的版本。
text类型在Symbian编程中基本不用,而一般采用描述符(descriptor)。TText默认是16位的。
typedef int TBool; 有两个枚举值:ETrue和EFalse。TBool变量最好不要直接和ETure和EFalse比较。如下:
TBool flag = ETrue;
if (flag) // if (!flag)
{
flag = EFalse;
}
对浮点数的支持视处理器而定,如果没有FPU,效率非常低,所以最好是不要用浮点数。 如果一定要用,尽量转化为整数操作。
typedef float TReal32; typedef double TReal64; typedef double TReal;
typedef void TAny;
TAny一般只用作指针,其他情况下用void比较好。
TAny* MyFunction(); void MyOtherFn();
TAny* 在很多Symbian API中都用到了,如:
static TUint8* Copy( TAny* aTrg, const TAny* aSrc, TInt aLength);
enum TState {EOff, Eon, EInit};
Enumeration类型应该以T开头,而枚举值应该以E开头。
TState state = GetState();
if (state == EOn)
{
//Do something here
}
T类:只包含值,而不包含指针以及外部的资源,在栈上分配空间。
TVersion osVersion = User::Version();
C类:所有需要分配内存的类都必须从CBase继承并且以C开头。
class CExample : public CBase
{
private:
CDesCArrayFlat* iArray;
}
CExample* example = new (ELeave) CExample;
R类:包含指向某个资源的handler。
RTimer timer;
timer.CreateLocal();
M类:定义一个接口,一般只包含纯虚函数,不包含成员数据,减少类之间的依赖,用来接受回调消息。
class MEikStatusPaneObserver
{
public:
virtual void HandleStatusPaneSizeChange() = 0;
}
任何实现MEikStatusPaneObserver接口的类都必须实现HandleStatusPaneSizeChange()函数。
Casting用于在类(classes)和类型(types)之间作转化,Symbian中仍然可以使用C中语法。
dynamic_cast:不支持,Symbian中没有RTTI。
static_cast:把一个基类转化为一个继承类。
TInt intValue = 0xff;
TUint8 byteValue = static_cast<TUint8>(intValue);
reinterpret_cast:把一个指针类型转化为另外一个指针类型,如integer转化为point类型或者相反。
TUint32 fourBytes = 0;
TUint8* bytePtr = reinterpret_cast<TUint8*> (&fourBytes);
bytePtr++;
*bytePtr = 0xFF;
const_cast:移除一个类的const属性。
内存管理
规则:
Always delete objects your class owns, from the class destructor.
Don't delete objects that you don't own (that is, those that you merely use).
Don't allocate twice (this will cause a memory leak).
Don't delete twice (this will corrupt the heap).
When you delete outside the destructor, immediately set the pointer to zero.
When you are reallocating, you must use the sequence 'delete, set pointer to zero,
allocate', just in case the allocate fails.
Use new(ELeave) rather than plain new.
Use L on the end of the name of any function that might leave.
Use traps where you need to ¨C for instance, when a function can't leave and must
handle errors privately ¨C and not where you don't.
Push an object to the cleanup stack if (1) that object is otherwise only referred to by an
automatic pointer, and (2) you are going to call a function that might leave during that
object's lifetime.
Never push a member variable to the cleanup stack ¨C use theiMember naming
convention.
Give all heap-based classes a name beginning with C, and derive them from CBase.
Trust CBase to zero initialize all data, including all pointers. Exploit CBase's virtual
destructor for cleanup purposes.
Never leave from a C++ constructor.
Put construction functions that might leave into a second-phase constructor such as
ConstructL().
Optionally, use NewL() and NewLC() to wrap up allocation and construction.
Symbian OS本身就是为内存和资源受限的设备开发的,应用程序运行过程中很可能碰到内存用光,或者硬件资源不可用的情况。而这种exceptions是通过修改程序无法解决的,所以遵守以下几条:
Stack:默认大小8kb,自动删除,如 TInt i = 0;
Heap :至少0.5Mb,由程序员手动删除,如 CMyObj* obj = new (ELeave) CMyObj;
首先介绍Conventional C++ Memory Management,在Symbian看来,这是非常低效率的。
在Symbian中推荐采用Leave,如果内存或者资源不能分配到,这个代码就会Leave,沿着Call Stack,直到操作系统或者在某个函数中被Handle掉。
所有可能Leave的函数最好以L结尾,保证该函数的用户知道这个函数可能Leave。
Leave的例子:
处理Leave:
操作系统有默认的处理Leave的方式:
开发者可以通过trap装置来处理Leave。TRAP(_r, _s)和TRAPD(_r, _s),其中:
TRAPD(err, DoFunctionL());
if (err != KErrNone)
{ //Error Handling }
else
{ //Everything is well }
cleanup stack用于存储在leave发生后需要deallocating的局部变量(指针)。即:当一个函数leave了,所有在cleanup stack上的对象会被全部删除掉。
Cleanup Stack的使用方法:
CleanupStack::PushL(ptr) :当发生leave时所有内存都会被释放
CleanupClosePushL(handle):当发生leave时这个句柄(handler)会被关闭
CleanupStack::Pop(pointer):第一个元素出栈
CleanupStack::PopAndDestroy(pointer):第一个元素出栈并释放内存
如果一个函数可能leave,检查一下两种情况:
CMyClass* CMyClass::NewL(TInt aBufSize)
{
CMyClass* self = new (ELeave) CMyClass;
CleanupStack::PushL(self);
self->ConstructL(aBufSize);
CleanupStack::Pop(self);
return self;
}
如果某个函数会在cleanup stack上留下一个对象,那么他必须以C结尾。
C++构造函数一定不能leave。所有内存和资源的分配应该在第二阶段构造函数ConstructL( )中完成。
编码指南,所有用户定义的C类必须:
Construction的规则:
Destruction的规则:
Further Discussion:
User::LeaveIfError(fs.Connect());
如果你的程序有内存泄露,在模拟器上关闭时会crash。尽早发现并解决你的内存泄露,因为你可以追查到你可能导致内存泄露的代码改动。如果实在找不到,可用下面方法:
Heap Balance Checking:
用上述这两个宏放在你要检查的代码的开头和结尾,如果发生panic,则说明这段代码中发生了内存泄露。可以嵌套使用。
Panic是一个未经处理的exception,暗示着一个无法解决的错误。
一般程序有以下三类错误:
可以使用trap和cleanup stack技术来解决环境和用户错误,但是对于第一类的程序错误,我们无法恢复,最好是使用User::Panic()函数,它带有两个参数,第一个是string,第二个是Tint。
描述符(Descriptors)封装了字符串和二进制数据,用于替代C中的以NULL结尾的字符串。它的长度和数据都封装在了描述符中,Symbian API中用的都是描述符。如:
TPtrC ptr (KHelloWorld); CEikonEnv::Static()->InfoMsg(ptr);
主要可以分为以下几类,其中带C的是不可修改的。
描述符可以是可修改的和不可修改的,通常带C的都是不可修改的,不可修改的是可修改的基类。
在描述符类后加上8或者16影响了存储在描述符中的数据的宽度,默认是16位的,处理二进制或ASCII时采用8位。
除Literal外的所有描述符的基类,提供了基本的接口和基础功能。他们本身不能实例化,一般用作函数参数。
TDesC:提供了比较、复制、搜索、提取部分字符串的函数。
TInt TDesCUtil::SumLengths(const TDesc& aDesC1, const TDesc& aDesC2)
{
return aDesC1.Length() + aDesC2.Length();
}
TDes:继承自TDesC,添加了许多用于修改数据的函数。其最大长度是描述符被创建时确定的。
TInt TDesCUtil::AppendL(TDesc& aTarget, const TDesc& aDesC)
{
TInt sumLen = aTarget.Length() + aDesC.Length();
if (aTarget.MaxLength() < sumLen)
{
User::Leave(KErrOverflow);
}
aTarget.AppendL(aDesC);
}
提供了一种将字符串放在只读存储空间中的机制(实际存放在程序的数据区,而不是真的在ROM中)。一般不采用TLitC而直接采用_LIT()宏。 _LIT(KHelloWorld, "Hello World!");
通过()操作符可以得到 const TDesC&。 TInt length = KHelloWorld().Length();
在函数参数为const TDesC&可以直接使用KHelloWorld。iLabel->SetTextL(KHelloWorld);
将数据作为本身的一部分存储在stack上,他们的最大长度是在编译时确定的。
TBuf<16> helloWorld = KHelloWorld;
TInt len = KHelloWorld().Length();
helloWorld[len-1]='?';
TBufC的用法如下:
_LIT(KHelloWorld, "Hello World");
const TInt maxBuf = 32;
TBufC<maxBuf> buf;
TInt currentLen = buf.Length(); // == 0
buf = KHelloWorld;
currentLen = buf.Length(); // == 11
TText ch = buf[2]; // == 'l'
TBuf的用法如下:
const TInt bufLen = 6;
TUInt8 objType = 1;
TUInt8 objId = 1;
TUInt8 xCoord = 128;
TUInt8 yCoord = 192;
....
TBuf8<bufLen> buf;
buf.Append(objType);
buf.Append(objId);
...
//we can now do something with the buffer such as writting it to a binary file or send via socket.
用于引用存储在其他地方的数据,如:
const unsigned char KBuffer[ ] = {0x00, 0x33, 0x66, 0x99, 0xbb, 0xff};
TPtrC8 bufferPtr( KBuffer, sizeof(KBuffer));
iSocket.Write(bufferPtr, iStatus);
TPtr的用法:
_LIT(KHelloWorld, "Hello World");
const TInt maxBuf = 32;
TBufC<maxBuf> buf;
buf = KHelloWorld;
TPtr ptr = buf.Des();
ptr[7] = 'a'; ptr[8] = 'l'; ptr[9] = 'e'; ptr[10] = 's';
CEikonEnv::Static()->InfoMsg(ptr); // "Hello Wales"
动态在堆(heap)上分配,通过HBufC的API,数据可以被set和reset,但是不能被修改。如:
HBufC* heapBuf = HBufC::NewL(KHelloWorld().Length());
*heapBuf = KHelloWorld();
delete heapBuf;
HBufC通常在以下几种情况下使用:
对HBufC中的内容进行修改:
_LIT(KHello, "Hello!");
_LIT(KWorld, "World!");
HBufC* heapBuf = HBufC::NewL(KHello().Length());
*heapBuf = KHello; //buf holds "Hello!"
heapBuf = heapBuf->ReAllocL(KHello().Length() + KWorld().Length());
CleanupStack::PushL(heapBuf);
TPtr ptr (heapBuf->Des()); //DON'T use TPtr ptr = heapBuf->Des(); this will set maxlen to 6 but not 12...
ptr[KHello().Length() - 1] = ' ';
ptr += KWorld;
iTopLabel -> SetTextL(ptr);
CleanupStack::PopAndDestroy();
DrawNow();
文章评论(0条评论)
登录后参与讨论