Please note that as of October 24, 2014, the Nokia Developer Wiki will no longer be accepting user contributions, including new entries, edits and comments, as we begin transitioning to our new home, in the Windows Phone Development Wiki. We plan to move over the majority of the existing entries. Thanks for all your past and future contributions.

Fundamentals of Symbian C++/Class Types & Declarations

From Wiki
Jump to: navigation, search
Article Metadata
Created: hamishwillee (10 Jan 2011)
Last edited: hamishwillee (23 Jul 2012)


Basic Types

Symbian C++ defines a set of fundamental types which, for compiler independence, are used instead of the native built-in C++ types. They are provided as a set of typedefs (within the file e32def.h) as follows:

Typedef Type Description
TInt signed int In general, the non-specific TInt or TUint types should be used, corresponding to signed and unsigned 32-bit integers respectively, unless there is a specific reason to use an 8- or 16-bit variant.
TInt8 signed char
TInt16 short int
TInt32 long int
TUint unsigned int
TUint8 unsigned char
TUint16 unsigned short int
TUint32 unsigned long int
TInt64 long long These use the available native 64-bit support.
TUint64 unsigned long long
TReal32 float

Use of floating-point numbers should generally be avoided unless they are a natural part of the problem specification. Many Symbian platform devices do not have a hardware floating-point unit: as a result, their floating-point performance is many times slower than integer performance.
TReal64 double

Most serious floating-point calculations require double-precision. All standard math functions (see the Math class) take double-precision arguments. Single-precision should only be used where space and performance are at a premium, and when their limited precision is acceptable.
TReal double
TAny void TAny* is used instead of void* to represent a pointer to an object of unspecified type. Note that void is still used to represent the 'nothing' return type.

Thus, we write the following to represent a function taking any object as a parameter and returning nothing:

void TypicalFunction(TAny* aPointerParameter);

in preference to the equivalent:

void TypicalFunction(void* aPointerParameter);

The following is not the same function, because the return type is a pointer to 'something':

TAny* TypicalFunction(TAny* aPointerParameter);

TBool int This type should be used for Booleans. For historical reasons, the Symbian C++ TBool type is equivalent to int ETrue=1 and EFalse=0. Since C++ will interpret any non-zero value as true, direct comparisons with ETrue should not be made.

Note how all these types begin with a capital letter T, which stands for Type, indicating a simple basic type. In addition to the above, simple classes are named beginning with a T as are enums, for example:

enum TFruit {EApple, EOrange, EPear};

The fundamental Symbian C++ types should always be used instead of the native types (that is, use TInt instead of int). However, there is one exception, as mentioned above – always use void when a method has no return type.

Class Types

Symbian C++ also defines several class types, each of which has different characteristics, such as where objects may be created (on the heap, on the stack) and how those objects should later be cleaned up. The conventions make the creation, use and destruction of objects more straightforward. When writing code, the required behavior of a class should be matched to the Symbian C++ class characteristics. Later, a user of an unfamiliar class can be confident in how to instantiate an object, use it and then destroy it without untoward side effects such as leaking memory.

When Symbian C++ was initially created, it had a native exception-handling mechanism (it now additionally supports standard C++ exception handling), which is called leaving; tied closely with leaving is the use of the cleanup stack and two-phase construction (these concepts are described in subsequent sections). The differing class-type characteristics arose as a consequence of these three mechanisms; by classifying and using classes according to a standard set of characteristics, system-wide and consistent behavior can be achieved.

T Classes

T classes are simple classes that behave much like the C++ built-in types. For this reason, they are prefixed with the same letter as the typedefs described in the table above.

When Symbian C++ was initially created, the destructor of a stack-based object was not called in the event of exception occurring. The T prefix was used to indicate a type of class that was safe to declare on the stack, in other words, a class that didn’t contain data members that required cleanup via a destructor. As a consequence, T classes didn’t have destructors. However, since Symbian v9.1 this is no longer the case. It is now possible for a T class to have a destructor that will get called when the object goes out of scope (though the destructor should not leave or throw an exception). This is discussed in more detail in The Implications of Leaving in a Destructor.

Locally created T-class objects are usually stack based but they can also be created on the heap – indeed, some T classes should only ever be heap based because the resulting object would otherwise occupy too much stack space. A rule of thumb is that if something is >256 bytes then it shouldn’t go on the stack (the default stack size is limited to only 8 Kb!).

Although the data contained in these classes is simple, some T classes can themselves have fairly complex APIs, such as the lexical analysis class TLex and the descriptor base classes TDesC and TDes, covered later. In other cases, a T class is simply a C-style struct consisting only of public data.

C Classes

C classes are only ever allocated on the heap. Unlike T classes, C classes own pointers to other objects, and have a destructor to clean up these member variables.

For memory management to work correctly, C classes must ultimately derive from the class CBase (defined in the Symbian C++ header file e32base.h). This class has three characteristics which are inherited by every C class:

  • Safe destruction: CBase has a virtual destructor, so a CBase-derived object is destroyed properly by deletion through a base-class pointer.
  • Zero initialization: CBase overloads the operator new to zero-initialize an object when it is allocated on the heap. This means that all member data in a CBase-derived object will be zero-filled when it is created, and this does not therefore need to be done explicitly in the constructor.
  • Private copy constructor and assignment operators. CBase classes declare these to prevent calling code from accidentally performing invalid copy operations.

On instantiation, a C class typically needs to call code which may fail. A good example is instantiation of an object that performs a memory allocation, which fails if there is insufficient memory available. This kind of failure is handled by what is called a leave, and is discussed in more detail in Leaves and the Cleanup Stack. A constructor should never be able to leave, because this can cause memory leaks. To avoid this, C classes use an idiom called two-phase construction. Two-phase construction is described in Object Construction.

R Classes

The R which prefixes an R class indicates that it holds an external resource handle, for example a handle to a server session. R classes are often small, and usually contain no other member data besides the resource handle.

While a C class directly allocates resources (for example memory), R classes cause the indirect allocation of resources. For example, in order to open a file the method RFile::Open() would be used. The RFile class does not itself open the file and consequently use resources directly. Instead, it has a handle to a class in the file server, which would directly open the file and use the resources.

Unlike the C class type, there is no equivalent RBase class (although many R classes derive from RHandleBase – this is especially important for resources which may need to be shared across threads and processes). A typical R class has a simple constructor and an initialization method typically called Open(), but sometimes also Create() or Initialize(), that must be called after construction to set up the associated class and store its handle as a member variable of the R class object.

R classes may exist as class members or as local stack-based variables; they are not intended to be created on the heap but can be – though doing so is unconventional and making them leave safe (see Leaves and the Cleanup Stack) becomes more complex. R objects must be made leave safe, if used in functions that may leave, by using the cleanup stack.

An R class must also have a method that can be used to release the resource (via the handle). Although in theory this cleanup function can be named anything, by convention it is almost always called Close(). A common mistake when using R classes is to forget to call Close() or to assume that there is a destructor which cleans up the owned resource. (CleanupClosePushL() is used to cleanup up the resource as a result of an exception, see Leaves and the Cleanup Stack.)

M Classes

The M prefix stands for mixin, where the class is used for defining interface classes. In Symbian C++, M classes are often used to define callback interfaces or observer classes. The only form of multiple inheritance encouraged on Symbian C++ is that involving multiple M classes.

An M class is an abstract interface class that declares pure virtual functions and has no member data.

Like a Java interface, an M class should usually have only pure virtual functions. However, there may be cases where non-pure virtual functions may be appropriate. A good example of this occurs when all the implementation classes of that interface have common default behavior. Adding a shared implementation to the interface reduces code duplication and its related bloat and maintenance headaches. Of course, there’s a restriction on what this default implementation can do, because the mixin class must have no member data. Typically, all virtual functions are implemented in terms of calls to the pure virtual functions of the interface.

When a class inherits from a CBase (or derived) class and one or more M classes, the CBase derived class must always be put first, due to the way the cleanup stack functions. That is:

class CCat : public CBase, public MDomesticAnimal

and not

class CCat : public MDomesticAnimal, public CBase

M class constructors and destructors

Since an M class is never instantiated and has no member data, there is never a need for an M class to have a constructor.

Sometimes C classes are referenced via a pointer to their parent M class, in which case they require a destructor (further details of this are given in Leaves and the Cleanup Stack).

If an M class does not have a virtual destructor, you may instead provide a pure virtual Release() method. It’s up to the implementing code to decide what this means (for a C class, the function can just call “delete this”). This makes the interface flexible — the implementation class can be stack- or heap-based, perform reference counting, special cleanup or whatever. It's a sensible approach because, in general, a mixin interface class should not be concerned with the implementation details of classes that inherit it. It isn’t essential to call your cleanup method Release() or Close(), but it can help your callers if you do. First of all, it’s recognizable and easy enough to guess what it does. More importantly, it enables the client to make use of the CleanupReleasePushL() or CleanupClosePushL() functions, as described here.

Static Classes

Some Symbian C++ classes contain only static member functions – the classes themselves cannot be instantiated; their functions must instead be called using the scope-resolution operator:

User::After(1000); // Suspend the current thread for 1000 microseconds.

Such classes typically provide utility functionality where the functions are collected together within a class for convenience, in a similar way as a namespace might be used. (These classes were created before the use of namespaces was standardized. Symbian C++ now supports namespaces and their use is preferable to using a static class. However, anonymous namespaces should not be used since they tend to generate different binaries each time they are compiled – a unique random name is generated for the anonymous namespace.) The naming convention for these classes is not to prefix with a significant letter. Examples include the User, Math and Mem classes.

Licence icon cc-by-sa 3.0-88x31.png© 2010 Symbian Foundation Limited. This document is licensed under the Creative Commons Attribution-Share Alike 2.0 license. See for the full terms of the license.
Note that this content was originally hosted on the Symbian Foundation developer wiki.

This page was last modified on 23 July 2012, at 07:45.
92 page views in the last 30 days.