Symbian学习--描述符

(一)描述符概述

Symbian自定义了一套系统底层解决方案用于处理字符串,它可以对内存溢出提供有效的保护。这套方案就是描述符。此外,Literal类型也是与字符串有关的类型,可以简单的将其理解为C语言中的static const char[]。Literal类型可以通过operator()方法轻松的转换成描述符。

描述符类族中提供多种类型的描述符,以满足各种需要,以下是描述符的类图:

clip_image002[4]

其中TDesC、TDes、TBufBase和TBufCBase都是不可实例化的类,我们真正使用的描述符类型是最下面一层的各种类型。而下面这张表将我们熟悉的C语言中的字符串和描述符作了一个近似的对照,供我们参考:

clip_image004

下面是6种描述符和Literal类型的用法:

const TInt KHelloWorldLength = 13;

//Use Literal

_LIT(KHelloWorld, "Hello World!/n");

console->Printf(KHelloWorld);

//Use TBuf

TBuf<KHelloWorldLength> tbuf(KHelloWorld);

console->Printf(tbuf);

//Use TBufC

TBufC<KHelloWorldLength> tbufc(KHelloWorld);

console->Printf(tbufc);

//Use HBufC

HBufC* hbufc = KHelloWorld().AllocL();

console->Printf(*hbufc);

//USe RBuf

RBuf rbuf;

rbuf.CreateL(KHelloWorld, KHelloWorldLength);

CleanupClosePushL(rbuf);

console->Printf(rbuf);

CleanupStack::PopAndDestroy();

//Use TPtrC

TPtrC tptrc(tbufc);

console->Printf(tptrc);

//Use TPtr

TPtr tptr = hbufc->Des();

console->Printf(tptr);

注意:HBufC是在堆上分配空间的类型,因此如果HBufC在创建之后,出现有可能异常退出的操作,那么需要将HBufC压入清空栈。本例中由于console->Printf和hbufc->Des()均不会异常退出,因此没有压栈。RBuf属于R类,同理。(这里为了强调RBuf的用法写出了清除栈操作,实际上是没有必要的)。

下面是描述符内存布局示意图:

clip_image005

调试上面代码可以在Carbide中看到iLength的十进制值,iLength是一个32位的TInt型值,它的最高4位表示存储描述符内存布局的类型,剩下的位表示描述符长度。也就是上图中Type和Length连接在一起组成的一个十进制数字。

clip_image007

为什么我们需要Type值:TDesC::Ptr()方法实际上是通过switch语句来识别描述符内存布局类型的,即通过前四位的Type硬性编码来识别,这样做是为了避免具体描述符作为TDesC或TDes参数传递时使用虚函数(虚函数对于性能开销较大)。通过Type值,TDesC就能知道子类的内存分布和类型而无需通过虚函数调用。

(二)栈描述符TBuf和TBufC

TBuf和TBufC都是模板化的数组,创建时采用一个整数值指定长度,TBuf可以在创建后在末尾继续Append数据,但TBufC不能直接Append,可以通过Des(),返回一个TPtr指针再进行Append操作。

TBufC比TBuf节省4字节(32位)的空间,因为它无需存储最大长度。但如果需要频繁修改TBufC,就需要调用Des(),这样又会产生性能上的开销,因此在大量使用TBuf和TBufC时我们需要权衡这两者的使用情况。

(三)指针描述符TPtr和TPtrC

注意:TPtr和TPtrC虽然都是指针,但它们不负责释放它们所指向的数据。

(1)TPtrC指向不同类型的数据:

// 1 Construction of a TPtrC from a literal

// ptr points to "Hello" in the program binary

_LIT(KTxtHello, "Hello");

TPtrC ptr(KTxtHello);

// 2 Setting a TPtrC to point to data in a stack descriptor

// ptr points to "World" in the buf descriptor variable

_LIT(KTxtWorld, "World");

TBufC<5> buf(KTxtWorld);

ptr.Set(buf);

// 3 TPtrC set to point to raw data held in a C-style array

// ptr points to "Hello" in the array variable

const TText array[6] = L"Hello";

ptr.Set(array, 5);

// 4 TPtrC set to point to a portion of data held in a descriptor

// ptr points to "or" in the buf descriptor variable

ptr.Set(buf.Mid(1,2));

(2)TPtr有两种类型:2(指向数据,数据可以在也可以不在描述符中)和4(指向描述符)。

TPtr类型2:所有的TPtr都是类型2,除非它们用HBufC::Des()或TBufC::Des()创建。

TText cStyleArray[] = L"Hello";4

// Creates a pointer to the array of data. The 2nd parameter is

// the length of the data pointed to and the 3rd parameter is

// the maximum length of the data pointed to.

// sizeof(cStyleArray) returns 12 which is the number of bytes,

// however the conceptual length of the data is 6, hence the

// division by 2.

TPtr ptrToRawData(cStyleArray, sizeof(cStyleArray)/2,

sizeof(cStyleArray)/2);

// copy "World" into the cStyleArray memory location

_LIT(KTxtWorld, "World");

ptrToRawData = KTxtWorld; // cStyleArray now contains "World"

// TPtrs provide safety when writing to raw memory as an attempt

// to write beyond the end of the data storage area results in

// a panic

ptrToRawData += KTxtWorld; // this will panic!

TPtr类型4:类型4的TPtr只能通过HBufC::Des()或TBufC::Des()创建。

注意:如果有多余一个的类型4 TPtr指向同一HBufC或TBufC,通过一个TPtr做的修改,不会在另一个中体现出来。

_LIT(KTxtHello, "Hello");

_LIT(KTxtString, "Overwrite me");

HBufC* hbuf = KTxtString().AllocL();

// hbuf contains "Overwrite me"

TPtr ptr = hbuf->Des(); // points to the hbuf contents

ptr = KTxtHello; // hbuf now contains "Hello"

delete hbuf;

(3)函数Set()

Set用于改变TPtr和TPtrC指向的内容。

_LIT(KHello, "Hello");

_LIT(KWorld, "World");

TPtrC hello(KHello); // contains "Hello"

TPtrC world(KWorld); // contains "World"

world.Set(hello); // world now contains "Hello"

声明TPtr并初始化为空:

TPtr ptr(NULL, 0);

. . .

ptr.Set(hbuf->Des());

TPtr作为类成员,在构造函数中初始化为空:

CSomeClass::CSomeClass() : iPtr(NULL, 0)

{}

. . .

iPtr.Set(hbuf->Des());

(四)堆描述符HBufC和RBuf

(1)HBufC的内存布局:

clip_image009

HBufC在堆中的分配以堆单元粒度为单位(Symbian系统指定的粒度),每当分配时,OS会自动调整HBufC的最大长度到下一个粒度边界,因此我们创建的HBufC多是比我们预期的要长。

注意:HBufC的长度数据没有保存在栈中,而是保存在对应的堆数据单元上。这样做是为了节省栈空间。

HBufC的创建:

_LIT(KTxtHello, "Hello");

HBufC* buffer = HBufC::NewL(32);

*buffer = KTxtHello;

//Another more efficient method to create HBufC

//HBufC* buffer = KTxtHello().AllocL();

delete buffer;

HBufC的重新分配:

可以通过ReAllocL重新分配HBufC,但如果有TPtr指向HBufC,则TPtr必须被重置,因为重新分配之后,HBufC会指向新的内存单元。

_LIT(KTxtHello, "Hello");

_LIT(KTxtGreeting, "How are you?");

HBufC* heapBuf = KTxtHello().AllocLC();

// place it on the cleanup stack

TPtr ptr(heapBuf->Des());

// ptr = KTxtGreeting; // would panic because the

// maximum length is exceeded

heapBuf = heapBuf->ReAllocL(KTxtGreeting().Length());

// So need to reallocate the heapBuf variable

CleanupStack::Pop();

// The address of heapBuf may have changed during ReAllocL

CleanupStack::PushL(heapBuf);

// so need to re-push it to the cleanup stack

ptr.Set(heapBuf->Des()); // and reset the pointer

ptr = KTxtGreeting;

// Now the assignment can be made safely

. . .

CleanupStack::PopAndDestroy();

(2)RBuf的内存布局:RBuf有两种类型:2(指向内存数据)和4(指向HBufC)

clip_image011

clip_image013

RBuf的创建:

RBuf myRBuf;

TBufC<20> myTBufC (_L("Descriptor data"));

myRBuf.CreateL(myTBufC);

或者

RBuf myRBuf;

_LIT(KGenesis, "In principio creavit Deus caelum et terram.");

TInt maxSizeOfData = 30;

myRBuf.CreateL(KGenesis(), maxSizeOfData); // trims text to 30 characters

RBuf的重新分配:(需要注意入清除栈和出栈的操作)

myRBuf.CleanupClosePushL();

. . .

const TInt newLength = myRBuf.Length() + appendBuf.Length();

if (myRBuf.MaxLength() < newLength)

{

myRBuf.ReAlloc(newLength);

}

myRBuf.Append(appendBuf);

. . .

CleanupStack::PopAndDestroy(); // calls myRBuf.Close();

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值