Important: The information in this document is obsolete and should not be used for new development.
TObject
Most of the classes in the MacApp class library descend from theTObject
class. TheTObject
class defines fields and methods that provide basic functions used by many subclasses. To avoid unnecessary storage in the classes that descend from it, theTObject
class defines a minimum of fields. In fact, its only fields serve as part of MacApp's RTTI support.The methods of the
TObject
class enable an object to
These services are described in the following sections.
- provide RTTI information about its class hierarchy
- initialize and free its data
- create and delete objects
- register as a dependent of another object and be notified of changes in the objects on which it depends
- write its data to, or read its data from, a stream destination of any type (a file stream, a handle stream, and so on)
- create a copy of itself
- compare itself to another object
- identify an operation that must be supplied by a subclass
RTTI Fields and Methods
MacApp supplies a number of macro definitions to make runtime type information available to your application, as described in "Runtime Type Information," beginning on page 27. MacApp'sMA_DECLARE_CLASS
macro is expanded to provide each class that uses it with a staticClassDesc
field:
static const ClassDesc fgClassDesc;Because the field is static (that is, one variable per class is instantiated, initialized at static init time, and accessed through the field), MacApp's RTTI support inserts just oneClassDesc
field per class, not one per object.When expanded, the
MA_DECLARE_CLASS
macro also defines theGetClassDescDynamic
method, which returns theClassDesc
field for the class. Each class that uses MacApp's RTTI macros overrides this method to return its ownClassDesc
field, so all subclasses ofTObject
are compatible for RTTI. This is important for operations such as determining whether an object belongs in a list of objects of a specific type.You don't normally call
GetClassDescDynamic
directly--instead you call one of the following methods:
You can also use the
GetClassID
- Gets the class ID from the
ClassDesc
fieldGetClassName
- Gets the class name from the
ClassDesc
fieldGetStandardSignature
- Gets the class signature from the
ClassDesc
fieldDescendsFrom
- Determines whether the object descends from the class specified by the passed
ClassDesc
GetSuperClass
- Returns the
ClassDesc
field for the object's immediate superclassIsSameClass
- Determines whether the object's
ClassDesc
information matches a specifiedClassDesc
MA_MEMBER
andMA_DYNAMIC_CAST
macros to access RTTI information, as shown in the following code fragment:
if (MA_MEMBER(theControl, TPopup)) ...; // Do something that requires theControl to descend from TPopup. MMailable* mailDoc = MA_DYNAMIC_CAST(MMailable, fDocument); if (!mailDoc) Failure(minErr, 0); // "...program error" if the document // can't be cast to the desired type.Initializing and Freeing Objects
MacApp uses a two-phase process for initializing objects. When you create a new object with thenew
routine, the first phase of initialization takes place automatically--the object's constructor routines are called (one for each class in the object's class hierarchy that has a constructor). Then you perform the second phase by calling the object's initialization method. An object's initialization method typically calls the initialization method of its parent object, and so on, up the class hierarchy. MacApp uses this two-phase initialization for objects it creates as well.You free an object by calling its
Free
method, or by calling the convenience routineFreeIfObject
, which is described later in this section. You can callFree
on any object that descends fromTObject
, sinceTObject
implements aFree
method.The following describes a useful approach for initializing and freeing objects in your application, using the class
TYourClass
as an example:
The
- When you define a class, supply it with a constructor routine that has the same name as the class itself (
TYourClass
). In the constructor, set fields of the object to default or safe values (such asNULL
). Don't do anything in the constructor that might fail, such as allocating memory--if a failure occurs in a constructor, the object may still have uninitialized references that make it unsafe to delete. That's why MacApp provides a two-phase initialization. (MacApp's failure mechanism is described in Chapter 3, "Core Technologies.")- If your class requires additional initialization, supply an initialization method. By convention, initialization methods start with an
I
, such asIYourClass
. If the parent class ofTYourClass
has an initialization method, you normally call it fromIYourClass
, often as the first action. In theIYourClass
method, do any memory allocation or other initialization that can't be safely done in the constructor routine.If
IYourClass
does anything that may fail, put failure handling in a wrapper routine that creates an instance of your object and calls the initialization method. If a failure occurs, your destructor routine (described in the next item) will free the memory from any successful memory allocations; if it is appropriate to free the object itself, the wrapper routine can do so, using theFreeIfObject
routine.- Supply your object with a destructor routine,
~TYourClass
. A destructor method frees any memory allocated in the initialization method and performs any other necessary cleanup. It has the same name as the class, preceded by a tilde symbol (~), and is called automatically whenever an instance of the class is freed with thedelete
routine. Don't call any failure methods in the constructor. Use theFreeIfObject
routine to free any objects allocated byTYourClass
.
FreeIfObject
routine calls theFree
method of the passed object. TheTObject::Free
method callsShallowFree
, which just callsdelete
to delete the object from the global object heap. Thedelete
operator is described in the next section.Your application may wish to prevent an object from being automatically freed when it is deleted. For example, you may be keeping track of references to an object and want to delete the object only when the reference count is zero. To prevent automatic freeing of a deleted object, you can imitate the MacApp class
TDeskScrapView
, which overrides theFree
method but doesn't necessarily callInherited
.Creating and Deleting Objects
MacApp provides a global heap object,gObjectHeap
, to allocate memory for objects your application creates with the globalnew
operator. TheTObject
class overridesoperator
new
andoperator
delete
to work with MacApp's global heap object. Theoperator
new
routine calls MacApp'sMAOperatorNew
routine, which in turn callsgObjectHeap->Allocate
. Theoperator
delete
routine calls theMAOperatorDelete
routine, which in turn callsgObjectHeap->Free
.The global heap object is described in more detail in Chapter 3, "Core Technologies."
Dependency Relationships
MacApp implements a system for managing dependency relationships between objects. It allows an object to be registered as a dependent of another object and to be notified of changes in the object on which it depends. MacApp's dependency system is described in detail in Chapter 3, "Core Technologies."The
TObject
class supplies dependency support for all its subclasses, and works with the global dependency object,gMacAppDependencies
, to support MacApp's dependency mechanism. The followingTObject
methods take part in dependency operations:
You can read more about these methods in the MacApp Class and Method Reference.
AddDependent
- Calls the global dependency space's
AddDependency
method to make the passed object a dependent of the current objectChanged
- Results in a call to
DoUpdate
for each dependent object (you call an object'sChanged
method when a change has occurred that should be reported to the object's dependents)DoUpdate
- Called when a notifier of the current object changes;
DoUpdate
does nothing inTObject
--subclasses add specific updating behaviorGetDependencySpace
- Accessor for the global dependency object,
gMacAppDependencies
RemoveDependent
- Calls the global dependency space's
RemoveDependency
method to remove the passed object as a dependent of the current objectRemoveAllDependencies
- Calls the global dependency space's
RemoveDependencies
method to remove any dependency relationships based on the current object;RemoveAllDependencies
is called by theTObject::Free
method ifRemoveDependenciesOnFree
returnsTRUE
RemoveDependenciesOnFree
- Returns
TRUE
inTObject
, indicating that any dependency relationships that include this object should be removed when the object is freedReading and Writing Streams
MacApp provides stream classes that are useful for transferring data between a variety of destinations, including files, memory, and resources.The
TObject
class provides methods for reading and writing stream data. Any subclass ofTObject
can override theWriteTo
method to write its data to a stream, and theReadFrom
method to read its data from a stream. MacApp's document classes use streams to write their data to disk.MacApp's streaming mechanism is described in detail in Chapter 3, "Core Technologies."
Cloning Objects
You can create a new object by copying an existing object with theClone
method, which is implemented inTObject
. Cloning produces an object whose fields have the same values as the original object. It can be an efficient way of duplicating an object without having to copy its values field by field.Implementation of Cloning
To implement cloning, theTObject::Clone
method callsTObject::ShallowClone
, which simply allocates a block of memory the size of the current object and uses the ToolboxBlockMove
routine to copy the current object's data into the new object.Classes that allocate memory or do any special handling need to override the
Clone
method. Typically, an override ofClone
callsInherited::Clone
and casts the returned object to the type of the current class. It then clones any owned objects, sets up any special relationships, and does any other task that can't be handled by simply copying the original object.Complications of Cloning
Cloning can become complicated when the cloned object refers to another object. For example, should the referred-to object also be cloned? Or should the new object point to the same object the original object does? The answer can vary, depending on how the object reference is used.Suppose, for example, you are cloning an object that has a reference to another object, and the referenced object logs an entry whenever the main object is accessed. You probably want to clone the log object as well, to keep a separate log for the new main object. You may also want to clear the log for the cloned object.
But suppose you are cloning an object that has a reference to a database object. Chances are you don't want to duplicate an entire database--you just want the original object and the new object to refer to the same database.
In still another case, suppose you are cloning a shape object that has a reference to a
TDrawingEnvironment
object. You might want all the shapes drawn in that environment to refer to the sameTDrawingEnvironment
object, or you might have reasons for keeping a separate copy with each shape.You should consider these issues when cloning an object in your application. By default, the
Clone
method copies the original object exactly, so all references in the new object will point to the same items as the corresponding references in the cloned object. If this is not the desired behavior, you must change it by overriding theClone
method in your class.
- Note
- Use of the
Clone
method may not be optimal for duplicating objects of typeTDocument
or its subclasses. You might instead call the application object'sDoMakeDocument
method to create a new document, then call a custom document-cloning method to copy specific information from the cloned document to the new document.Comparing Objects
TheTObject
class provides several methods to test for object equality and inequality. TheIsSame
method compares the object to the passed object reference and returnsTRUE
if the object references match.The
CompareObject
method and all of theTObject
class operator comparison methods (operator!=
,operator<
,operator<=
,operator==
,operator>
, andoperator>=
) call theIsLessThan
,IsGreaterThan
, andIsEqual
methods to compare the object to the passed object reference. TheIsLessThan
,IsEqual
, andIsGreaterThan
methods do nothing inTObject
, so you must override them if you wish to use them or to useCompareObject
or the operator comparison methods.
- Note
- Except for determining that two objects with the same numerical reference value are the same object, MacApp makes no definition of what object equality or inequality means. If you override MacApp's operator comparison methods, you should define these terms to have meaning in your application.
The SubClassResponsibility Method
TheSubClassResponsibility
method reports that a method was called that should have been overridden by a subclass. This method can be useful during development as a reminder that a routine has not yet been implemented. TheSubClassResponsibility
method reports only in debug versions of the application--otherwise it does nothing.