General rules for implementation classes

To be reusable, the implementation class deals with objects at the level common to all types that your template can be instantiated with. For maximum reuse, your implementation should be void; for rare cases it can be a more specific type. For polymorphism, refer to the objects using pointers (void*) .

NOTE Because it is contrary to the normal style rules, you should not use a pointer to pass an argument to a method that is not going to alias or own the argument. But, because void& is illegal in C++, you must use void* in all such cases--even though the method is not going to alias or own the argument.

An implementation class does not have enough information to perform type-specific operations, so it delegates these operations to a specialized class. The specialized class' methods cast the void* arguments back to the correct type and perform the type-specific operation. Because these casts are blind casts, the object must always be cast to and from the same type. If you pass in the object as a base class, but extract it as a derived class, the C++ compiler will not perform the pointer fix-up, and you will end up with an incorrect pointer value.

      class TBase { ... };
      class TDerived : public TBase, virtual public VVirtual { ... };
      void FunctionTakingTBaseAsVoid(void* item);
      
      void f()
      {
          TBase base;
          TDerived derived;
          FunctionTakingTBaseAsVoid(item);                    // Correct
          FunctionTakingTBaseAsVoid(d&erived);                // Incorrect
          FunctionTakingTBaseAsVoid((TBase*) &derived);       // Correct
      }
      
      void FunctionTakingTBaseAsVoid(void* item)
      {
          TBase* asBase = (TBase*) item;                      // Correct
          TDerived* asDerived = (TDerived*) item;             // Incorrect
      
    
          TDerived* asDerived = (TDerived*) (TBase*) item;    // Correct, conditionally
      }
The C++ compiler catches most attempts to perform type-specific operations on void objects; however, the delete operator is the one exception. Deleting a pointer to an object that is held as a void* silently releases the storage that the object occupies, but does not call that object's destructor.

      {
          void* baseAsVoid = new TBase(...);
      
          delete baseAsVoid;                  // Incorrect. Destructor not called.
          TBase* base = (TBase*) baseAsVoid;
          delete base;                        // Correct
      }

[Contents] [Previous] [Next]
Click the icon to mail questions or corrections about this material to Taligent personnel.
Copyright©1995 Taligent,Inc. All rights reserved.

Generated with WebMaker