diff -r 000000000000 -r 89d6a7a84779 Symbian3/SDK/Source/GUID-F686965A-13E5-5C0A-AED1-55EC91C79433.dita --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Symbian3/SDK/Source/GUID-F686965A-13E5-5C0A-AED1-55EC91C79433.dita Thu Jan 21 18:18:20 2010 +0000 @@ -0,0 +1,95 @@ + + + + + +Restrictions +on the use of leaves and TRAPs in destructorsOutlines the restrictions on the use of leaves and TRAPs in destructors. +

You cannot use TRAP (and TRAPD) +and User::Leave() in a destructor, nor in any function +that a destructor might call, for objects that have been placed onto the call +stack. TRAP and User::Leave() are implemented +in terms of C++ Try, Catch and Throw. This means that if an exception occurs, +the call stack is "unwound". In doing this, the destructors of objects on +the call stack are called. If any of these destructors subsequently throw +an exception, this is termed a nested exception. While a nested exception +is supported on the emulator (WINS), it is not supported on other hardware, +and there is no guarantee that the thread will terminate cleanly.

+

Symbian coding standards state that objects placed onto the call stack +should not have non-trivial destructors. By convention, such classes are identified +by names having an initial T or R. This means that such destructors should +not leave.

+

The following code fragment shows the kind of code that is not safe to +use:

+void f() + { + TBar bar; + // Function processing. + ... + // The bar object on the call stack is automatically destroyed + // at function termination. + } + +TBar::~TBar() + { + // The destructor calls a function that can leave. + // This is not permitted here. + doCleanUpL(); + } +

You can use TRAP (and TRAPD) +and User::Leave() in a destructor and in any function that +a destructor might call, for heap-based objects, i.e. objects that are destroyed +by the cleanup stack. However, while this is permissible, it is not recommended +practice.

+

Such objects are instances of classes derived from CBase, +and by convention such classes are identified by names having have an initial +C.

+

In principle, a destructor should never fail. If a destructor can leave, +or a function called by the destructor can leave, it suggests that the code +has been poorly architected. It also implies that part of the destruction +process might fail, potentially leading to memory or handle leaks.

+

One possible approach to avoid using functions that can leave within a +destructor is to have what might be referred to as ‘two-phase destruction’ +where some form of ShutdownL function is called prior to +deleting the object. If there are concerns about introducing additional complexities +in API-usage (and greater risk), suitable guards can be introduced. One method +might be to store the current state of the object internally and then use +an ASSERT to check this in the destructor. This would ensure that any usage +errors are discovered by very simple run time testing.

+

The following code fragment shows an example of two-phase destruction.

+// Here, the shutdown function is a member of the class CMyClass, +// and performs destruction activity that can leave. + +class CMyClass : public CBase + { +public : + IMPORT_C ~CMyClass(); + IMPORT_C void ShutdownL(); + ... +private : + TBool iStateActive + ... + } + +EXPORT_C void CMyClass::ShutdownL() + { + if (iStateActive == ETrue) + { + // Some destruction activity that can leave. + iStateActive = EFalse; + } + } + +EXPORT_C CMyClass::~CMyClass() + { + // Assert to ensure that ShutdownL() has already been called. + ASSERT(iStateActive == EFalse); + } + +
\ No newline at end of file