Topic: constructing an object with virtual function call to derived classes


Author: davie@c-plusplus.de (davie)
Date: Tue, 10 Aug 2004 06:12:17 GMT
Raw View
only an immature idea:
(following problem:
  struct Base {
        Base () { bar (); }
        virtual void bar () { ... }
  }
  struct Derived : Base {
        void bar () {...}
  }
  Derived d;

  of course, Base::bar will be called. but what, if the programmer
wants    any (Derived)::bar to be called instead of it?
)
the ideas on http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.4
are good, but not perfect.
i got another idea and wanted to ask you for your opinion.

  class LateCall;
  class LateCallHolder { //only a dumb smart pointer
    LateCall *fun;
  public:
    LateCallHolder () : fun(0) {}
    ~LateCallHolder ();
    LateCallHolder (LateCall *f) : fun(f) {}
    LateCall &reset (LateCall *f); //with this special ability
  };

Of course, you have to pay sth:

  class Base {
    Base () {}
    friend class LateCall;
  public:
    Base (LateCall& late_this);
    virtual ~Base () {}
    virtual void bar () { cout << "Base::bar()\n"; }
  };

define two classes instead of one:

  class LateCall : public Base {
    Base *obj;
    map<string,bool> callreqs;
  public:
    LateCall (Base *b) : obj(b) {}
    void bar () { callreqs["bar"] = true; }
    ~LateCall () {
      if (callreqs["bar"]) obj->bar();
    }
  };

  LateCall &
  LateCallHolder::reset (LateCall *f) {
    delete fun;
    fun = f; return *f;
  }

  LateCallHolder::~LateCallHolder () {
    delete fun;
  }

LateCall has to implement all virtual and pure virtual functions.
but instead of implementing a specific behavior, it (1) delegates the
calls to
another Base object. and (2): it calls the functions when being
destroyed.
a few things have to be done: the order in which the functions are
called
maybe with sth like boost::function. remember: this is only an odd
example of what I mean.

Next: implement an example:

  Base::Base (LateCall &late_this) {
    late_this.bar();           //calls bar for this, but in an obscure
way it will call Derived::bar (introduced later)
    cout << "Base::Base()\n";  //will execute immediately
  }

  class Derived : public Base {
  public:
    Derived (LateCallHolder = LateCallHolder()); //important!
    //this has to be a default argument in order to get the right
lifetime for the object.
    void bar () { cout << "Derived::bar()\n"; }
  };

  Derived::Derived (LateCallHolder later)
  : Base(later.reset(new LateCall(this))) //now, this is tricky: bind
"this" to the LateCall object
  {
    cout << "Derived::Derived()\n";
    //at this point, all functions called in Base will be executed,
due to the LateCall object being destroyed (remember: it executes the
functions on destruction, an therefore it calls Derived->base)
  }

  int main () {
    Derived d;
  }

thus, the output is:

  Base::Base()
  Derived::Derived()
  Derived::bar()

as expected.

i hope that i didn't miss some rules but i think it will compile
(can't try it at the moment) and run without undefined behavior. stick
the pieces together and try.
i try to emulate a simple two phase construction but avoiding that
awful init() function.
i think the main problem is the order of the function calls.
what do you think about it?

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]