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?
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.