[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

#### 4.4.1.3 Classes Implementing Multiple Interfaces

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:

- Via C++ multiple inheritance.
- Via object embedding, in which an object that implements another interface is embedded within the "carrier" object.

To simplify the following discussion we'll refer them as multiple interface and embedded interface schemes.

#### Multiple Interfacing

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

#### Embedded Interfaces

Another way to implement several interfaces in one object is to embed an object that implements some interface into 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 the ``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;
      friend MyHalo;

      // Declare the iBumpMap embedded interface.
      class MyBumpMap : public iBumpMap
      {
        SCF_DECLARE_EMBEDDED_IBASE(MyGraphics3D);
        ...
      } scfiBumpMap;
      friend MyBumpMap

    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, and has another special characteristic: It does not use a reference count, as embedded interfaces may never delete themselves. `IncRef()` and `DecRef()` of embedded objects simply call the corresponding function of their ``scfParent'`, which is the object in which they are embedded.

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

    SCF_IMPLEMENT_EMBEDDED_IBASE (MyGraphics3D::MyHalo)
      SCF_IMPLEMENTS_INTERFACE (iHalo)
    SCF_IMPLEMENT_EMBEDDED_IBASE_END

    SCF_IMPLEMENT_IBASE (MyGraphics3D::MyBumpMap)
      SCF_IMPLEMENTS_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 appropriate values (zero and ``this'`). Likewise, you should destroy embedded interfaces with the `SCF_DESTRUCT_EMBEDDED_IBASE()` macro. Here is how to do it:

    MyGraphics3D::MyGraphics3D(iBase* iParent)
    {
      SCF_CONSTRUCT_IBASE(iParent);
      SCF_CONSTRUCT_EMBEDDED_IBASE(scfiHalo);
      SCF_CONSTRUCT_EMBEDDED_IBASE(scfiBumpMap);
      ...
    }

    MyGraphics3D::~MyGraphics3D()
    {
      ...
      SCF_DESTRUCT_EMBEDDED_IBASE(scfiBumpMap);
      SCF_DESTRUCT_EMBEDDED_IBASE(scfiHalo);
      SCF_DESTRUCT_IBASE();
    }

You should not call `SCF_CONSTRUCT_IBASE()` within embedded object's constructor since all work needed to initialize ``iBase'` fields is done in the carrier object's constructor. Likewise, you should not call `SCF_DESTRUCT_IBASE()` within the embedded object's destructor.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ]

This document was generated using texi2html 1.76.