开发者

How can one enforce calling a base class function after derived class constructor?

I'm looking for a clean C++ idiom for the following situation:

class SomeLibraryClass {
  public:
    SomeLibraryClass() { /* start initialization */ }
    void addFoo() { /* we are a collection of foos */ }
    void funcToCallAfterAllAddFoos() { /* Making sure this is called is the issue */ }
};
class SomeUserClass : public SomeLibraryClass {
  public:
    SomeUserClass() {
      addFoo();
      addFoo();
      addFoo(); //开发者_如何学编程 SomeUserClass has three foos.
    }
};
class SomeUserDerrivedClass : public SomeUserClass {
  public:
    SomeUserDerrivedClass() {
      addFoo(); // This one has four foos.
    }
};

So, what I really want is for SomeLibraryClass to enforce the calling of funcToCallAfterAllAddFoos at the end of the construction process. The user can't put it at the end of SomeUserClass::SomeUserClass(), that would mess up SomeUserDerrivedClass. If he puts it at the end of SomeUserDerrivedClass, then it never gets called for SomeUserClass.

To further clarify what I need, imagine that /* start initialization */ acquires a lock, and funcToCallAfterAllAddFoos() releases a lock.

The compiler knows when all the initializations for an object are done, but can I get at that information by some nice trick?


I would probably implement this with a factory of some sort. The following code should be read as pseudocode, I haven't tried compiling it or anything.

class LibraryClass
{
public:
   template<typename D>
   static D *GetNewInstance()
   {
      // by assigning the new D to a LibraryClass pointer, you guarantee it derives from LibraryClass at compile time
      // that way, the user can't accidentally type "LibraryClass::GetNewInstance<int>()" and have it work
      LibraryClass *c = new D();
      c->funcToCallAfterAllAddFoos();
      return c;
   }

   ...
};


I'm not sure this is possible. However, you could redesign this a little bit: give your base class constructor an argument std::vector<Foo> const &foosToBeAdded, and let derived classes pass the correct foos:

class SomeLibraryClass {
  public:
    SomeLibraryClass(std::vector<Foo> const &foosToBeAdded) {
      /* start initialization */
      std::for_each(foosToBeAdded.begin(), foosToBeAdded.end(),
                    std::bind1st(std::mem_fun(&SomeLibraryClass::addFoo), this));
      funcToCallAfterAllAddFoos();
    }
  private:
    void addFoo(Foo const &someFoo) { /* we are a collection of foos */ }
    void funcToCallAfterAllAddFoos() { /* this is now called at the right time */ }
};

class SomeUserClass : public SomeLibraryClass {
  public:
    SomeUserClass() :
      SomeLibraryClass(makeAFooVector())
    {
    }
  private:
    std::vector<Foo> makeAFooVector() { /* return a vector with three Foos */ }
};

The pattern can be extended by letting the SomeUserClass constructor also receive a vector of Foos. It would then add its own Foos to the list before calling the base class constructor.

You could also pass iterators instead of vectors. Left as an exercise.


As C++ does not allow reflection, no, you cannot directly obtain this information. (Though there may be some way I'm unaware of to prevent compilation from succeeding)

However, I question the design here. Wouldn't it make more sense to release the lock once SomeLibraryClass is done? If you're concerned about the efficiency of calling AddFoo multiple times your library can provide a member accepting a std::vector<Foo> which would only have to acquire and release the lock once.


Try the Non-virtual Interface Idiom. Make your public method non-virtual, but have it call a private virtual method. The derived classes override this private virtual method (yes, derived classes can override private virtuals). Put the lock stuff in the public non-virtual method around the call to the private virtual method.

EDIT: After looking at the code more carefully, I think you might be better off having a constructor in the base class that accepts a container of Foo objects and adds them.


Why provide a public addfoo method at all? You say it is all initialization, so let the collection get paassed to the constructor.

Then you can call the nonvirtual functocall from the constructor

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜