Search code examples
c++inheritanceprivate-membersdefensive-programming

Can I prevent a derived class from calling its own functions? (C++)


Consider the following base class, which will count how many times I call CallDerivedFunction:

class B {
  public:
    B() {numCalls = 0;}
    void CallDerivedFunction() {numCalls++; DerivedFunction();}
    int GetNumCalls() {return numCalls;}
  private:
    virtual void DerivedFunction() = 0;
    int numCalls;
};

People other than me will be writing derived classes that implement DerivedFunction. However, I'm trying to count how many times DerivedFunction gets called.

Is there any way prevent a derived class D from directly calling its own implementation of DerivedFunction, eg. disallow:

class D : public B {
  private:
    void DerivedFunction() {
      if(<some condition>) DerivedFunction(); // I don't want to allow this.
      if(<some condition>) CallDerivedFunction(); // This is OK.
    }
};

Note: OK, of course this is a simplification; the more general question is, when my base class is supposed to be functioning as a manager and doing things like tracking usage patterns, etc for a collection of derived classes, can I force the derived classes to call through the manager class? Or must I rely on derived-class developers to do the right thing here?


Solution

  • Yes, you can do this - I believe this is called the pass-key idiom.

    You add a "key" helper class to your base-class that only the base class can instantiate (i.e. ctor is private, but friend of the base-class), but that the derived class can see. Then you add a reference to this helper class to the signature of the function you wish to "lock" so that only the base-class may call it - since it is the only class that can instantiate the "key".

    class A
    {
       public:
         class Key {Key(); friend ::A;};
         // Only A can call this, since only A can instantiate A::Key
         virtual void Foo(A::Key&)=0; 
    };
    
    
    class B:public A
    {
      public:
        virtual void Foo(A::Key&) override
        {
          // B can override this...
        }
    
        void Bar()
        {
          Key k; // <- this wont compile, so you cannot call Foo from B
          this->Foo(k);
        }
    };
    

    Note that this doesn't prevent recursion like in your example. But you could probably get that to work by using a value instead of a reference and disabling the copy constructor of the key.