News:

This week IPhone 15 Pro winner is karn
You can be too a winner! Become the top poster of the week and win valuable prizes.  More details are You are not allowed to view links. Register or Login 

Main Menu

C++ tips - Envelope/letter class example

Started by ben2ong2, October 07, 2006, 03:55:26 AM

Previous topic - Next topic

0 Members and 4 Guests are viewing this topic.

ben2ong2

PROBLEM: You are not allowed to view links. Register or Login (Lee D. Coller), 6 Sep 92


The following classes represent I problem I have encountered.

class A {
public:
    void func() = 0;
};

class B : public A {
private:
    int _i;
public:
    void func();
};

class C: public B {
private:
    double _d;
public:
    void func();
};

class D {
private:
    /* some information from which to construct a B or C */
public:
    A getA() const;        // ideally
};

The idea is that the member function D::getA() would be able to construct a
B or C and return it to the caller as an A.  Because of the semantics, most
likely D::getA() will have to create a temporary, so just returning a
reference to some pre-existing B or C will not work.

Of course returning an A won't work because the pertinent information in
either B or C is lost, and won't even compile because A is abstract.

The simple solution is to allocate the B or C off the heap, with the caller
of D::getA() responsible for deleting it.  I don't like this solution
because it is inelegant and the subclass of B or C is usually not kept
around outside the scope of the caller.

The obvious solution is to use a class which can be Garbage Collected, but
C++ currently has no such thing.

The best solution I could come up with is to create another class to handle
managing a pointer to A such as the following.

class AHandler {
private:
    A* _a;
public:
    AHandler(A* a) { _a = a; a->ref(); }    // use reference counting GC
    ~AHandler(A* a) { _a->unref(); }

    AHandler operator=(const AHandler &a)
   { a._a->ref(); _a->unref(); _a = a._a; }

    A* operator->() const { return _a; }
    operator a&() const { return *_a; }
    a& operator*() const { return *_a; }
};

Then the member function getA() would return an AHandler, that it created
using a B or C allocated off the heap.  Class A would implement reference
counting (and probably a copy member that return an A*), and AHandler
would take care of management of the A*.  The operators would allow AHandler
to be treated like an A* or an A, depending on the context (in the actual
implementation, A includes some overloaded operators for comparison, thus
the operator a&() conversion).

My question is, I would like to be able to do this without having A handle
reference counting.  Can anyone think of a good method?  Am I overlooking
some simple, elegant solution?  Also, is this what is referred to as a
"Smart pointer class?"


RESPONSE: You are not allowed to view links. Register or Login (Bob Martin), 7 Sep 92

Take a look at "Advanced C++ Programming Styles and Idioms" by James
O. Coplien in the section entitled "Envelope Classes and Delegated
Polymorphism".  Here he talks about your problem and provides a
solution.

The idea is to provide class A with a A* member, and a member
functions that can determine whether a B or C is needed.  The needed
class is placed in the A* pointer of the A object.  All other method
calls of the A objects are explicitly deferred to the A* object.

For example:

class A
{
  public:
     A() {itsA = 0;}
     A(some criteria)
     {
       if (some criterion)
         itsA = new B(...);
       else
         itsA = new C(...);
     }

     virtual ~A() {delete itsA;}

     virtual int DoSomething(int x) {return itsA->DoSomething(x);}

  private:
     A* itsA;
};

class B : public A { public: B(); int DoSomething(int);};
class C : public A { public: C(); int DoSomething(int);};

Now, whenever you construct an A object, whether from the heap, or on
the stack, or even statically; if you supply a criterion, the
appropriate B or C will be silently constructed and saved by the A
object.  When you invoke the methods of A (DoSomething), those methods
are simply deferred to the saved B or C object.  When the A object is
destructed, the B or C is also automatically destroyed.

This method works well, but has one glaring fault.  A must know about
B and C.  If more derivatives of A are needed later, A must change to
support them in its constructor. 
You are not allowed to view links. Register or Login
You are not allowed to view links. Register or Login