1. Trang chủ
  2. » Công Nghệ Thông Tin

Symbian OS Explained Effective C++ Programming for Smartphones phần 2 doc

40 453 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 40
Dung lượng 297,33 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

When compiling Symbian OS code, the compilers are explicitly directed to disable C++ exceptionhandling, and any use of the try, catch or throw keywords is flagged to leak memory inadverte

Trang 1

are classes in Symbian OS code itself which do not fit the ideals I’ve put

to you above There are a few classes in Symbian OS which don’t evenconform to the naming conventions Two well-documented exceptionsare the kernel-side driver classes and the heap descriptor (HBufC), which

is discussed further in Chapter 5

This doesn’t mean that the code is wrong – in many cases there aregood reasons why they do not fit the theory In the lower-level code, inparticular, you’ll find cases which may have been written before the nameconventions were fully established or which, for efficiency reasons, havedifferent characteristics and behavior Whenever you come across a newclass, it’s worth comparing it to the rules above to see if it fits and, if not,considering why it doesn’t You’ll be able to add a set of good exceptions

to the rules to your list of cunning Symbian OS tricks, while discardingany code that unnecessarily contravenes the conventions – thus learningfrom others’ mistakes

1.8 Summary

This chapter reviewed the major class types used when coding forSymbian OS, and described their main features such as any specialrequirements when constructing or destroying objects of the class,whether they can be stack- or heap-based and typical member datacontained by the class (if any) In particular, the chapter discussed theuse of the class name conventions to indicate the cleanup characteristics

of objects of the class in the event of a leave

The guidelines within this chapter should be useful when writing aclass – they can help you save time when coding and testing it, if only bycutting down on the rewrite time If you can stick as closely as possible

to Symbian OS conventions, your clients will know how you mean yourclass to be constructed, used and destroyed This naturally benefits them,but can also help you reduce documentation and support-time furtherdown the track

Trang 2

Leaves: Symbian OS Exceptions

Go away I’m all right

Said to be the last words of H G Wells

Symbian OS was first designed at a time when exceptions were not part

of the C++ standard Later, exception handling was introduced to thestandard, but was found to add substantially to the size of compiled codeand to run-time RAM overheads, regardless of whether or not exceptionswere actually thrown For these reasons, standard C++ exception handlingwas not considered suitable to add to Symbian OS, with its emphasis on

a compact operating system and client code When compiling Symbian

OS code, the compilers are explicitly directed to disable C++ exceptionhandling, and any use of the try, catch or throw keywords is flagged

to leak memory inadvertently in the event of a leave So when and howdoes a leave occur and why would you use it?

Trang 3

exception and propagate an error value back up the call stack to a point

at which it can be ”caught” by a trap harness and handled appropriately

To all intents and purposes, code execution ends at the point of the leaveand resumes where it is trapped The leave sets the stack pointer to thecontext of a trap harness TRAP macro and jumps to the desired programlocation, restoring the register values It does not terminate the flow ofexecution (unlike an assertion, which is used to detect programmingerrors and panic accordingly, as described in detail in Chapter 16)

TRAP and User::Leave() may be considered analogous to thestandard library setjmp() and longjmp() methods respectively Acall to setjmp() stores information about the location to be ”jumpedto” in a jump buffer, which is used by longjmp() to determine thelocation to which the point of execution ”jumps” A leave should only beused to propagate an exception to a point in the code which can handle

it gracefully, unwinding the call stack as it does so It should not be used

to direct the normal flow of program logic

A typical leaving function is one that performs an operation that isnot guaranteed to succeed, such as allocation of memory, which mayfail under low memory conditions Since leaving functions by definitionleave with an error code (a ”leave code”), they do not also need to returnerror values Indeed any error that occurs in a leaving function should bepassed out as a leave; if the function does not leave it is deemed to havesucceeded and will return normally Generally, leaving functions shouldreturn void unless they use the return value for a pointer or reference

to a resource allocated by the function Later in this chapter, I’ll discussthe factors that may influence your decision as to whether to implement

a function that leaves or one that returns an error value

Some examples of leaving function declarations are as follows:

Functions may leave if they:

• call code that may leave without surrounding that call with atrap harness

• call one of the system functions that initiates a leave, such asUser::Leave()or User::LeaveIfError()

Trang 4

• use the overloaded form of operator new which takes ELeave as aparameter (described in Section 2.2).

The suffix notation for names of functions which may leave is asimplification of the C++ exception specification which uses throw( .)

or throw(type) by convention to indicate a function which may throw

an exception A call to User::Leave() or User::LeaveIfError()

is similar to a C++ throw instruction (except for its destruction of based variables, as I’ll discuss shortly) while the TRAP macros are, ineffect, a combination of try and catch

stack-User::LeaveIfError()tests an integer parameter passed into itand causes a leave (using the integer value as a leave code) if the value isless than zero, for example, one of the KErrXXX error constants defined

in e32std.h User::LeaveIfError() is useful for turning a leaving function which returns a standard Symbian OS error into one thatleaves with that value

non-User::Leave() doesn’t carry out any value checking and ply leaves with the integer value passed into it as a leave code.User::LeaveNoMemory() also simply leaves but the leave code ishardcoded to be KErrNoMemory which makes it, in effect, the same ascalling User::Leave(KErrNoMemory)

sim-User::LeaveIfNull() takes a pointer value and leaves withKErrNoMemoryif it is NULL It can sometimes be useful, for example,

to enclose a call to a non-leaving function which allocates memory andreturns a pointer to that memory or NULL if it is unsuccessful

The following example shows four possible leaves:

TInt UseClanger(CClanger* aClanger); // Forward declaration

CClanger* InitializeClangerL() {

CClanger* clanger = new (ELeave) CClanger(); // (1) Leaves if OOM CleanupStack::PushL(clanger); // (2) See Chapter 3 clanger->InitializeL(); // (3) May leave User::LeaveIfError(UseClanger(clanger)); // (4) Leaves on error CleanupStack::Pop(clanger);

If a function may leave, its name must be suffixed with ”L”.

Trang 5

2.2 Heap Allocation Using new (ELeave)

Let’s take a closer look at the use of new (ELeave) to allocate an object

on the heap This overload leaves if the memory is unavailable, and thusallows the returned pointer to be used without a further test that theallocation was successful We saw it used in the code fragment above:

CClanger* InitializeClangerL() {

CClanger* clanger = new (ELeave) CClanger();

CClanger* clanger = new CClanger();

if (clanger) { CleanupStack::PushL(clanger);

clanger->InitializeL();

CleanupStack::Pop(clanger);

} return (clanger);

}

What exactly does new (ELeave) do? Well, when you call new() toallocate an object on the heap you are invoking the new operator Thisfirst allocates the memory required for the object by calling operatornew(yes, the naming scheme is incredibly confusing), passing in the size

of the memory required It then calls the constructor to initialize an object

in that memory This code is generated by the compiler, because it’s notpossible to call a constructor directly – which of course means that if youwant an object constructed on the heap, you must allocate it, via the newoperator, through a call to new()

Symbian OS has overloaded the global operator new to take aTLeaveparameter in addition to the size parameter provided implicitly

by new() The TLeave parameter is ignored by operator new and

is only used to differentiate this form of operator new from the leaving version The Symbian OS overload calls a heap allocation functionthat leaves if there is insufficient heap memory available:

non-// From e32std.h enum TLeave {ELeave};

Trang 6

inline TAny* operator new(TUint aSize, TLeave);

// e32std.inl inline TAny* operator new(TUint aSize, TLeave) {return User::AllocL(aSize);}

Symbian OS has overloaded the global operator new to take a TLeave parameter This overload leaves if memory is unavailable

on the heap.

If a leaving function which allocates an object doesn’t leave, the allocation was successful and there is no need to check the result further.

2.3 Constructors and Destructors

Before moving on to talk further about how to call leaving functions,let’s consider which functions shouldnot leave Quite simply, neither aconstructor nor a destructor should leave, since doing so would potentiallyleak memory and place the object upon which it is invoked in anindeterminate state

Chapter 4 will discuss this in more detail, but essentially, if a structor can fail, say, through lack of the resources necessary to create orinitialize the object, you must remove the code that may leave from theconstructor and use the two-phase construction idiom instead

con-Likewise, a leave should not occur in a destructor or in cleanupcode One particular reason for this is that a destructor could itself becalled as part of cleanup following a leave and a further leave at thispoint would be undesirable, if nothing else because it would mask theinitial reason for the leave More obviously, a leave part-way through adestructor will leave the object destruction incomplete which may leakits resources

If a destructor must call a leaving function, it may potentially betrapped and the leave code discarded, although this causes problemswhen testing out of memory leaves using the debug macros described inChapter 17 Generally, it’s preferable to have a separate leaving function,which can be called before destruction, to perform actions that may failand provide the caller an opportunity to deal with the problem beforefinally destroying the object Typically these would be functions such asCommitL()or FreeResourceL()

Trang 7

Constructors and destructors must not leave.

2.4 Working with Leaving Functions

Let’s look at the practicalities of working with leaves Below is anexample of a call to a leaving function You’ll notice that there is noneed to check that ironChicken is initialized before using it, sinceCTestClass::NewL()would have left if any failure had occurred

void FunctionMayLeaveL() {

// Allocates ironChicken on the heap CTestClass* ironChicken = CTestClass::NewL();

// If NewL() didn’t leave, ironChicken was allocated successfully ironChicken->FunctionDoesNotLeave();

delete ironChicken;

}

If the CTestClass::NewL() function leaves for some reason, it

is the responsibility of that function to release any memory alreadyallocated as part of the function If successful, the function allocates,initializes and returns a heap-based object (NewL() functions and two-phase construction are discussed in more detail in Chapter 4) In thecode above, a call to a non-leaving function follows, but consider theimplications if a leaving function was called instead For example:

void UnsafeFunctionL() {

// Allocates test on the heap CTestClass* test = CTestClass::NewL();

test->FunctionMayLeaveL(); // Unsafe – a potential memory leak!

delete test;

}

This is unsafe Memory is allocated on the heap in the call to Class::NewL(), but the following function may leave Should thisoccur test will not be deallocated; consequently the function has thepotential to leak memory In a scenario such as this, you should push theheap object onto the cleanup stack, which will delete it should a leaveoccur The cleanup stack is described more fully in Chapter 3

CTest-While heap variables referred to only by local variables may beorphaned in this way, member variables will not suffer a similar fate

Trang 8

(unless their destructor neglects to delete them when it is called at somelater point) Thus the following code is safe:

void CTestClass::SafeFunctionL() {

iMember = CClangerClass::NewL(); // Allocates a heap member FunctionMayLeaveL(); // Safe

}

Note that the CTestClass object (pointed to by ”this” in Class::SafeFunctionL()) is not deleted in the event of a leave.The heap-based iMember is stored safely as a pointer member variable,

CTest-to be deleted at a later stage with the rest of the object, through theclass destructor

I’ve shown that you must prevent leaks from the potential orphaning

of heap-based local variables, but what about cleanup of stack variables

if a leave occurs? The leave mechanism simply deallocates objects onthe stack – it does not call any destructors they have defined as it does

so, unlike a C++ throw Stack objects that own a resource which must

be deallocated, or otherwise ”released” as part of destruction, would leakthat resource in the event of a leave Classes which are intended to beused on the stack must not need a destructor

This is the reason why Symbian OS has a class naming tion which clearly defines the allowed usage of a class (described inChapter 1) The only classes which may be instantiated and used safely

conven-on the stack are T classes, which the Symbian OS naming cconven-onventiconven-ondictates must not have a destructor, and R classes, which do not have adestructor but use Close(), or a similar method, to free the associatedresource The cleanup stack must be used to ensure that this method iscalled in the event of a leave – I’ll discuss how to do so in the next chapter

class TMyClass {

TInt localInteger = 1; // Leave-safe (built-in type) FunctionMayLeaveL(localInteger);

TMyClass localObject(localInteger); // Leave-safe object AnotherPotentialLeaverL(localObject);

}

Let’s consider what happens if you happen to have a local able, an object of a T class, on the heap In a leaving function, you

Trang 9

vari-still need to protect the heap memory from being orphaned by a leavebut the object itself has no destructor You’ll recall that I mentionedearlier that the cleanup stack performs both destruction and dealloca-tion upon the objects in its care in the event of a leave Well, that’strue for objects of class types which have destructors, but for T classobjects, it simply deallocates the memory There’s more about this inChapter 3.

void AnotherFunctionL() {

TMyClass* localObject = new (ELeave) TMyClass();

// Make localObject leave-safe using the cleanup stack CleanupStack::PushL(localObject);

AnotherPotentialLeaverL(localObject);

CleanupStack::PopAndDestroy(localObject);

}

2.5 Trapping a Leave Using TRAP and TRAPD

Symbian OS provides two macros, TRAP and TRAPD, to trap a leave.The macros differ only in that TRAPD declares a variable in which theleave error code is returned, while the program code itself must declare

a variable before calling TRAP Thus the following statement:

TRAPD(result, MayLeaveL());

if (KErrNone!=result) // See footnote 2 {

// Handle error }

2 Throughout this text, you’ll notice that I prefer to use ”back to front” comparisons in

my if statements to prevent accidentally typing only a single =, which is valid C++ but isn’t

at all what is intended Take the following example of the unforeseen consequences that can arise from this bug, which is often difficult to spot unless you have a helpful compiler that warns you about it.

TInt ContrivedFunction() { // The bug in this function means it will always return 0 for (TInt index = 0; index < KContrivedValue; index++) {

TInt calculatedValue = DoSomeComplexProcessing(index);

// This assignment always returns true

if (calculatedValue=KAnticipatedResult) return (index);

} return (KErrNotFound);

}

However, not everybody likes this style of coding If you prefer not to use this technique,

it pays to compile with a high warning level and pay attention to any resulting warnings.

Trang 10

is equivalent to:

TInt result;

TRAP(result, MayLeaveL());

if (KErrNone!=result) {

// Handle error }

TRAPD(result, MayAlsoLeaveL()) }

User::LeaveIfError(result);

In the example, two TInt result variables are declared, one for eachTRAPDstatement The scope of the second result macro is bounded bythe curly brackets that enclose it Thus any leave code assigned to thesecond result variable, from the call to MayAlsoLeaveL(), is discarded

on exiting the bounding brackets The User::LeaveIfError() callthus only tests the leave code from the MayLeaveL() call, which isunlikely to be what the code intended To ensure both values are tested,the second TRAPD should be replaced with a TRAP – thus reusing theTIntresult declared by the initial TRAPD

If a leave occurs inside the MayLeaveL() function, which is cuted inside the harness, the program control will return immediately

exe-to the trap harness macro The variable result will contain theerror code associated with the leave (i.e that passed as a parameter

to the User::Leave() system function) or will be KErrNone if noleave occurred

Any functions called by MayLeaveL() are executed within the trapharness, and so on recursively, and any leave that occurs during theexecution of MayLeaveL() is trapped, returning the error code intoresult Alternatively, TRAP macros can be nested to catch and handleleaves at different levels of the code, where they can best be dealt with.I’ll discuss the runtime cost of using trap harnesses shortly, but if you findyourself using the TRAP macros several times in one function, or nesting aseries of them, you may want to consider whether you can omit trappingall the leaving functions except at the top level, or change the layout ofthe code

For example, there may be a good reason why the following functionmust not leave but needs to call a number of functions which may leave

Trang 11

At first sight, it might seem straightforward enough simply to put each call

in a trap harness

TInt MyNonLeavingFunction() {

TRAPD(result, FunctionMayLeaveL());

if (KErrNone==result) TRAP(result,AnotherFunctionWhichMayLeaveL());

if (KErrNone==result) TRAP(PotentialLeaverL());

// Handle any error if necessary return (result);

in comparison The combination of these factors does make a TRAP quite

an expensive way of managing a leave You should attempt to minimizethe number of TRAPs you use in your code, where possible, either byallowing the leave to propagate to higher-level code or by making anadjustment similar to the following:

MyNonLeavingFunction() {

3 A kernel executive call is made by user-side code to allow it to enter processor privileged mode in order to access kernel resources in a controlled manner Control is switched to the kernel executive, and the processor is switched to supervisor mode, within the context of the calling thread.

Trang 12

code is returned from a call to the package of functions, it will not beclear which function left.

Every program (even a simple ”hello world” application) must have atleast one TRAP, if only at the topmost level, to catch any leaves that arenot trapped elsewhere If you are an application writer you don’t need

to worry about this, though, because the application framework provides

I mentioned earlier that, under normal circumstances, you shouldn’timplement functions which both return an error and have the potential

to leave For example, consider a function, OpenFileObjectL(), thatinstantiates an object (CFileObject) which must be initialized with anopen file handle The implementation of this object may be in a separatelibrary, the source code for which may not be available to the writer ofthis code

TInt OpenFileObjectL(CFileObject*& aFileObject) {// File server session initialization is omitted for clarity // (handle is leave-safe)

RFile file;

TInt error = file.Open( );

if (KErrNone==error) {

CleanupClosePushL(file); // Makes handle leave-safe // Propagates leaves from CFileObject

void ClientFunctionL() {// Pass all errors up to be handled by a higher-level TRAP harness CFileObject* fileObject = NULL;

TInt errorCode=OpenFileObjectL();

if (KErrNone!=errorCode) {

User::Leave(errorCode);

}

}

Trang 13

Or they may use a TRAP to catch any leaves and return them as errors:

TInt ClientFunction() {

CFileObject* fileObject = NULL;

TInt errorCode;

TRAPD(r, errorCode=OpenFileObjectL());

if (KErrNone!=r) return (r);

if (KErrNone!=errorCode) return (errorCode);

}

Neither of these options is very attractive Furthermore, if the clientfunction can actually handle some of the errors at that point in the code, itbecomes even messier Should it differentiate between the two methods ofpropagating an error? And if so, how? Are the leaves in some way different

to the errors returned? Could the same error sometimes be returned as anerror value (say KErrNotFound from RFile::Open()) and sometimes

as a leave code (say a leave from CFileObject::NewL() whichattempts to read the contents of the file and finds it is missing some vitalconfiguration data)?

In effect, this approach requires you to document the function clearly

to allow callers to use it correctly and handle errors or exceptions as theychoose Maybe you can guarantee that you, or whoever takes responsi-bility for maintaining this function, keeps the documentation up to date.But you are returning an error directly from one, separate, component(class RFile) and a leave code from another (class CFileObject) Even

if you know exactly what errors and leave codes each will use and areprepared to document them for callers of OpenFileObjectL(), youcannot guarantee that their error handling will not change

It is preferable to restrict the code either to leaving or to ing an error First, consider an implementation that leaves under allcircumstances (notice that I’ve changed the signature to return theCFileObjectrather than pass it as a reference-to-pointer parameter)

return-CFileObject* LeavingExampleL() {

Trang 14

com-void ClientFunctionL() {

CFileObject* fileObject = NULL;

TRAPD(r, fileObject = LeavingExampleL());

switch (r) { case (KErrNoMemory):

// Free up some memory and try again

Otherwise it can just call the function directly and allow a higher-leveltrap handler to handle all the leave codes:

CFileObject* fileObject = LeavingExampleL();

As an alternative to leaving on all errors, the function to instantiate

a CFileObject could TRAP the call to CFileObject::NewL() andinstead return an error for all failures:

TInt Example(CFileObject*& aFileObject) {

RFile file;

TInt error = file.Open( );

if (error == KErrNone) {

TRAP(error, aFileObject = CThing::NewL(file));

} return error;

}

Again, if the calling code can handle some errors, it can switch on thereturn code; otherwise, it can call the function inside User::LeaveIf-Error()to pass all failures up to a TRAP harness in higher-level code

CFileObject* fileObject = NULL;

Trang 15

Bibliogra-TRAP, even if none of the callers can handle errors and they all callUser::LeaveIfError()as shown in the previous code sample.

However, if you know who all your callers are (for example, if thefunction is internal to your component) and you know that the callerswill all TRAP the call, it may be worthwhile implementing the versionthat returns an error This limits the use of a TRAP to one place inthe code

Use of TRAP is an expensive way of managing a leave in terms

of executable size and execution speed You should attempt to minimize the number of TRAPs you use in your code where possible However, every program must have at least one TRAP to catch any leaves that occur.

2.6 LeaveScan

LeaveScan is a useful tool which you should run regularly against yoursource code It checks that all functions which have the potential to leaveare named according to the Symbian OS convention, with a trailing L.LeaveScan can be used on your source to indicate areas of code whereyou may have forgotten to use the convention By revealing where leavesmay occur but are not acknowledged by the function name, it highlightspotential bugs and gives you an opportunity to fix the problem and ensurethat your code handles any leaves correctly

LeaveScan works by examining each line of source code and checkingthat functions which do not terminate in L cannot leave However, thereare a few functions (more accurately, operators) in Symbian OS thatmay leave but cannot have an L suffix (such as operator<< andoperator>> for RWriteStream and RReadStream respectively).The naming convention cannot be applied appropriately to operatorsand, unfortunately, LeaveScan does not have the sophisticated logicneeded to recognize operators that may leave When you use operatorsthat you know have the potential to leave, you’ll have to remember tocheck this code by sight yourself

LeaveScan also checks functions which do have a trailing L to see

if they really can leave If functions are encountered which do notleave, LeaveScan raises a warning However, this scenario can be per-fectly valid, for example, when implementing an abstract function such

as CActive::RunL(), some implementations may leave but othersmay not

Trang 16

LeaveScan will highlight functions which may leave but are rectly named without a suffixed ”L” The potential to leave occurs when a function:

incor-• calls other functions which themselves leave (and thus have function names ending in L) but does not surround the function call with a TRAP harness

• calls a system function which initiates a leave, such as

to catch the leave In fact, a TRAP harness is effectively a combination oftryand catch

Having compared leaves and TRAPs with standard C++, it’s worthmaking a comparison with the standard library too TRAP and leaveare analogous to the setjmp() and longjmp() methods, respec-tively – setjmp() stores information about the location to be ”jumpedto” in a jump buffer, which is used by longjmp() to direct the code tojump to that point

On Symbian OS, if a function can leave, that is, fail under exceptionalconditions, it indicates this by suffixing its function name with L Of allthe Symbian OS naming conventions, this is one you should comply withbecause, if you don’t, it’s hard to know whether you can call a functionwithout potentially orphaning any local heap-based variables You cancheck that you have adhered to the naming convention by running theLeaveScan tool over your source code

A function can leave if it calls one of the system functions whichcause a leave (such as User::Leave()), calls another leaving function(such as NewL()) or allocates memory using the Symbian OS leavingoverload of operator new Some functions should not leave, namelyconstructors (I’ll discuss the reasons behind this in detail in Chapter 4,

Trang 17

and explain how two-phase construction can be used to prevent it)and destructors, which could potentially leak memory by leaving beforecompleting object cleanup.

This chapter described best practice for writing and calling leavingcode, particularly when a function may have a return value for initializedpointer data It also discussed the best use of the TRAP and TRAPDmacros, which can have a significant overhead in terms of code size andruntime speed

The next chapter discusses the use of the cleanup stack to protectheap-based local variables against orphaning in the event of a leave

Trang 18

The Cleanup Stack

Life is pleasant Death is peaceful It’s the transition that’s troublesome

Jimi Hendrix

This chapter discusses a fundamental part of Symbian OS, the cleanupstack Symbian OS is designed to perform well with limited memory,and part of that design must inevitably consider memory managementwhen errors occur The cleanup stack manages memory which wouldotherwise be ”orphaned” (leaked) in the event of a leave

But what, exactly, is ”orphaning”? In the previous chapter, I describedwhy Symbian OS doesn’t use standard C++ exceptions, but insteadhandles exceptional conditions using ”leaves” Code that can leave is, atsome level, surrounded by a TRAP, or TRAPD, harness If a leave occurs,control is transferred directly to the statement following the harness Ineffect, the TRAP is equivalent to a setjmp and the leave to a longjmp.The stack memory is freed as the stack unwinds, but otherwise thestack frame is abandoned and no object destructors are called (which

is unlike a standard C++ exception) This means that the destructors ofany local variables or arguments passed by value, or objects created asmember variables of either of these, will not be called Some objectsare ”leave-safe” – they do not need destructors and contain only datawhich can be destroyed as stack memory is freed by the leave Thisdata may consist of the basic, built-in types or other objects whichcontain such types In Symbian OS these are called T classes (T for

”type”, described in detail in Chapter 1), the characteristic of which isthat they may safely be created on the stack in functions where codemay leave

If a local variable is a pointer to an object on the heap, when a leaveoccurs the pointer is destroyed without freeing that heap memory, whichbecomes unrecoverable, causing a memory leak The memory is said to

be orphaned This means that C class objects, which are always created

on the heap, as described in Chapter 1, are not leave-safe Unless theyare otherwise accessible for safe destruction (for example, as membervariables of an object which is destroyed regardless of the leave), the

Trang 19

memory they occupy on the heap and any resources they own areorphaned by a leave R class objects are generally not leave-safe either,since the resources they own must be freed in the event of a leave (through

a call to the appropriate Close() or Release() type function) If thiscall cannot made by an object accessible after the leave, the resource

is orphaned

Consider the following code, which creates an object of a C class(CClanger) on the heap, referenced only by an automatic variable,clanger After creating the object, a function which may potentiallyleave, InitializeL(), is called The heap-based object is not leave-safe and neither the heap memory it occupies, nor any objects it owns,are destroyed if InitializeL() leaves.1

void UnsafeFunctionL() {

CClanger* clanger = new (ELeave) CClanger();

clanger->InitializeL(); // Potential leaving function orphans clanger // Omitted for clarity

delete clanger;

}

You could remove the potential memory leak by placing a TRAP, orTRAPD, macro around the call to InitializeL() to catch any leaves.However, as Chapter 2 explained, the use of TRAPs should be limited,where possible, to optimize the size and run-time speed of the compiledbinary I have only shown one call which could cause the object to beleaked; in a typical function there may be other operations that mayleave It would be inefficient to surround each with a TRAP From anerror-handling perspective, too, it’s often preferable to leave rather thanreturn an error, as I discussed in Chapter 2

When you combine both arguments, and consider the following deemably inefficient code, it’s clear that there should be an alternative tothe TRAP idiom to protect objects which are not inherently leave-safe:

irre-void SafeButInefficientL() {

CClanger* clanger = new (ELeave) CClanger();

TRAPD(r,clanger->InitializeL()); // leave-safe, at a cost

is known as two-phase construction, and is described further in Chapter 4.

Trang 20

{ delete clanger; // delete clanger to prevent a leak User::Leave(r); // leave with the same error horrible!

}

// Now do something else that may leave TRAP(r, clanger->DoSomethingElseL());

if (KErrNone!=r) {

delete clanger; // Again, delete clanger to prevent a leak User::Leave(r);

free-3.1 Using the Cleanup Stack

This is the cue for the cleanup stack, which is accessed through the staticmember functions of class CleanupStack, defined in e32base.h:

class CleanupStack {

public:

IMPORT_C static void PushL(TAny* aPtr);

IMPORT_C static void PushL(CBase* aPtr);

IMPORT_C static void PushL(TCleanupItem anItem);

IMPORT_C static void Pop();

IMPORT_C static void Pop(TInt aCount);

IMPORT_C static void PopAndDestroy();

IMPORT_C static void PopAndDestroy(TInt aCount);

IMPORT_C static void Check(TAny* aExpectedItem);

inline static void Pop(TAny* aExpectedItem);

inline static void Pop(TInt aCount, TAny* aLastExpectedItem);

inline static void PopAndDestroy(TAny* aExpectedItem);

inline static void PopAndDestroy(TInt aCount,

TAny* aLastExpectedItem);

};

Objects that are not otherwise leave-safe should be placed on thecleanup stack before calling code that may leave This ensures they aredestroyed correctly if a leave occurs; in the event of a leave, the cleanup

Ngày đăng: 13/08/2014, 08:21

TỪ KHÓA LIÊN QUAN