The fundamental rules for instantiating and using Symbian classes.
Symbian C++ 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. Symbian uses a simple naming convention which prefixes the class name with a letter (T, C, R, or M).
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 for the built-in C++ types.
void LeavingFunctionL() { // T object declared on the stack. TExample example; // Call a function that can leave. DoSomethingL(); }When Symbian C++ was initially created, the destructor of a stack-based object was not called in the event of a leave 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 do not have destructors.
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.
A C class uses public inheritance,
and must ultimately derive from CBase
. A C class
never inherits directly from more than one C class. This is to avoid
the problems that can arise from multiple inheritance of implementation
in C++.
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 Symbian exception
handling. 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. This means that the constructor is guaranteed
not to leave and that any initialization that might leave is deferred
until a second phase constructor, usually called ConstructL()
.
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 an object managed by 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()
that must be called after construction to set up the associated
resource and store its handle as a member variable of the R class
object.
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()
. To ensure that Close()
is called even if a leave occurs, R objects often need to be added
to the cleanup stack using the function CleanupClosePushL()
. See Using
the cleanup stack for an example.
ConstructL()
methods.