[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Sometimes we will want one object to implement several interfaces. For example, the 3D graphics driver could provide a basic `iGraphics3D' interface and several additional optional interfaces such as `iHalo', `iBumpMap', and so on. There are two ways to do this:
To simplify the following discussion we'll refer them as multiple interface and embedded interface methods.
You can declare a class to inherit from several interfaces at once. This is useful if all implemented interfaces will need to access often same member variables. For example, both the `iHalo' and `iBumpMap' implementations will need to access many member variables from `iGraphics3D' such as pixel format, screen dimensions and so on. You declare such classes this way:
class MyGraphics3D : public iGraphics3D, public iHalo, public iBumpMap { public: ... }; |
When implementing the methods imposed by the `iBase' interface you should
use the IMPLEMENT_INTERFACE()
macro once for each implemented
interface:
SCF_IMPLEMENT_IBASE(MyGraphics3D) SCF_IMPLEMENTS_INTERFACE(iGraphics3D) SCF_IMPLEMENTS_INTERFACE(iHalo) SCF_IMPLEMENTS_INTERFACE(iBumpMap) SCF_IMPLEMENT_IBASE_END |
Another way to implement several interfaces in one object is to embed an
object that implements some interface into the another object, known as the
"carrier" object. This method is preferable when a secondary interface
seldom needs access to the carrier object's members, since you will need to
access them through scfParent
pointer. Here is an example:
class MyGraphics3D : public iGraphics3D { // Declare the iHalo embedded interface. class MyHalo : public iHalo { SCF_DECLARE_EMBEDDED_IBASE(MyGraphics3D); ... } scfiHalo; // Declare the iBumpMap embedded interface. class MyBumpMap : public iBumpMap { SCF_DECLARE_EMBEDDED_IBASE(MyGraphics3D); ... } scfiBumpMap; public: SCF_DECLARE_IBASE; ... }; |
Note that you don't need to use special names for classes; use anything you want (that is, the `My' prefix is not required; you could name the above class `GraphixThreeDeeImplementation' if you like; this is true for embedded classes as well). But most SCF macros that have the word `EMBEDDED' within their names expect embedded object names to follow the form `scfInterfaceName'. For instance, `scfiBase', `scfiTest', `scfiGraphics3D', and so on.
When you declare the `iBase' methods within an embedded class, you can
use the SCF_DECLARE_EMBEDDED_IBASE(OuterClass)
macro instead of
`SCF_DECLARE_IBASE'. In this case the `scfParent' member will be of
type OuterClass*
rather than of type iBase*
; this will
allow the member class to talk with its parent directly, thus allowing for
direct member variables and functions access. In fact, the
`SCF_DECLARE_IBASE' macro uses SCF_DECLARE_EMBEDDED_IBASE(iBase)
.
`SCF_DECLARE_EMBEDDED_IBASE' itself has another special characteristic:
It does not use a reference count, as embedded interfaces may never delete
themselves. IncRef
and DecRef
simply call the corresponding
function of their scfParent
, which is the object they are embedded in.
When using SCF_DECLARE_EMBEDDED_IBASE
, you should implement the
methods with the SCF_IMPLEMENT_EMBEDDED_IBASE()
macro instead of
SCF_IMPLEMENT_IBASE()
.
SCF_IMPLEMENT_IBASE(MyGraphics3D) SCF_IMPLEMENTS_INTERFACE (iGraphics3D) SCF_IMPLEMENTS_EMBEDDED_INTERFACE(iHalo) SCF_IMPLEMENTS_EMBEDDED_INTERFACE(iBumpMap) SCF_IMPLEMENT_IBASE_END |
And finally, in the parent object's constructor you should initialize all
embedded interface objects with the
SCF_CONSTRUCT_EMBEDDED_IBASE(InterfaceName)
macro. This will
initialize `scfRefCount' and `scfParent' fields within
`scfInterfaceName' member variables to appropiate values (zero and
`this'). Here is how to do it:
MyGraphics3D::MyGraphics3D(iBase* iParent) { SCF_CONSTRUCT_IBASE(iParent); SCF_CONSTRUCT_EMBEDDED_IBASE(iHalo); SCF_CONSTRUCT_EMBEDDED_IBASE(iBumpMap); ... } |
You should not call SCF_CONSTRUCT_IBASE()
within embedded object's
constructor (in fact, it can not have a constructor at all) since all work
needed to initialize iBase
fields is done in the carrier object's
constructor.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |