Overriding public virtual functions with private functions in C++
Asked Answered
D

7

68

Is there is any reason to make the permissions on an overridden C++ virtual function different from the base class? Is there any danger in doing so?

For example:

class base {
    public:
        virtual int foo(double) = 0;
}

class child : public base {
    private:
        virtual int foo(double);
}

The C++ faq says that it is a bad idea, but doesn't say why.

I have seen this idiom in some code and I believe that the author was attempting to make the class final, based on an assumption that it is not possible to override a private member function. However, This article shows an example of overriding private functions. Of course another part of the C++ faq recommends against doing so.

My concrete questions:

  1. Is there any technical problem with using a different permission for virtual methods in derived classes vs base class?

  2. Is there any legitimate reason to do so?

Demetricedemetris answered 27/1, 2009 at 18:24 Comment(1)
re-inventing "protected" are we?Irmgard
L
40

The problem is that the Base class methods are its way of declaring its interface. It is, in essence saying, "These are the things you can do to objects of this class."

When in a Derived class you make something the Base had declared as public private, you are taking something away. Now, even though a Derived object "is-a" Base object, something that you should be able to do to a Base class object you cannot do to a Derived class object, breaking the Liskov Substitution Prinicple

Will this cause a "technical" problem in your program? Maybe not. But it will probably mean object of your classes won't behave in a way your users expect them to behave.

If you find yourself in the situation where this is what you want (except in the case of a deprecated method referred to in another answer), chances are you have an inheritance model where inheritance isn't really modeling "is-a," (e.g. Scott Myers's example Square inheriting from Rectangle, but you can't change a Square's width independent of its height like you can for a rectangle) and you may need to reconsider your class relationships.

Lossa answered 27/1, 2009 at 19:0 Comment(0)
H
49

You do get the surprising result that if you have a child, you can't call foo, but you can cast it to a base and then call foo.

child *c = new child();
c->foo; // compile error (can't access private member)
static_cast<base *>(c)->foo(); // this is fine, but still calls the implementation in child

I suppose you might be able to contrive an example where you don't want a function exposed, except when you are treating it as an instance of the base class. But the very fact that that situation pops up would suggest a bad OO design somewhere along the line that should probably be refactored.

Heredia answered 27/1, 2009 at 18:27 Comment(1)
@mlvljr: But as an astronomical event I'm pretty awesome!Heredia
L
40

The problem is that the Base class methods are its way of declaring its interface. It is, in essence saying, "These are the things you can do to objects of this class."

When in a Derived class you make something the Base had declared as public private, you are taking something away. Now, even though a Derived object "is-a" Base object, something that you should be able to do to a Base class object you cannot do to a Derived class object, breaking the Liskov Substitution Prinicple

Will this cause a "technical" problem in your program? Maybe not. But it will probably mean object of your classes won't behave in a way your users expect them to behave.

If you find yourself in the situation where this is what you want (except in the case of a deprecated method referred to in another answer), chances are you have an inheritance model where inheritance isn't really modeling "is-a," (e.g. Scott Myers's example Square inheriting from Rectangle, but you can't change a Square's width independent of its height like you can for a rectangle) and you may need to reconsider your class relationships.

Lossa answered 27/1, 2009 at 19:0 Comment(0)
K
8

There's no technical problem, but you will end up with a situation where the publicly available functions will depend upon whether you have a base or derived pointer.

This in my opinion would be weird and confusing.

Kareykari answered 27/1, 2009 at 18:26 Comment(0)
K
4

It can be very useful if you are using private inheritance - i.e. you want to reuse a (customized) functionality of a base class, but not the interface.

Kiehl answered 27/1, 2009 at 19:17 Comment(0)
P
3

It can be done, and very occasionally will lead to benefits. For example, in our codebase, we are using a library that contains a class with a public function that we used to use, but now discourage the use of due to other potential problems (there are safer methods to call). We also happen to have a class derived from that class which a lot of our code uses directly. So, we made the given function private in the derived class in order to help everyone remember not to use it if they can help it. It doesn't eliminate the ability to use it, but it will catch some uses when the code tries to compile, rather than later in the code reviews.

Palinode answered 27/1, 2009 at 18:41 Comment(0)
E
1
  1. No technical problem, if you mean by technical as there being a hidden runtime cost.
  2. If you inherit base publically, you shouldn't do this. If you inherit via protected or private, then this can help prevent using methods that don't make sense unless you have a base pointer.
Embarkment answered 27/1, 2009 at 18:42 Comment(0)
S
1

A good use-case for private inheritance is a Listener/Observer event interface.

Example code for the private object:

class AnimatableListener {
  public:
    virtual void Animate(float delta_time);
};

class BouncyBall : public AnimatableListener {
  public:
    void TossUp() {}
  private:
    void Animate(float delta_time) override { }
};

Some users of the object want the parent functionality and some want the child functionality:

class AnimationList {
   public:
     void AnimateAll() {
       for (auto & animatable : animatables) {
         // Uses the parent functionality.
         animatable->Animate();
       }
     }
   private:
     vector<AnimatableListener*> animatables;
};

class Seal {
  public:
    void Dance() {
      // Uses unique functionality.
      ball->TossUp();
    }
  private:
    BouncyBall* ball;
};

This way the AnimationList can hold a reference to the parents and uses parent functionality. While the Seal holds a reference to the child and uses the unique child functionality and ignoring the parent's. In this example, the Seal shouldn't call Animate. Now, as mentioned above, Animate can be called by casting to the base object, but that's more difficult and generally shouldn't be done.

Scourge answered 5/1, 2017 at 19:26 Comment(1)
Listener/Observer interface, receiving callbacks.Ruysdael

© 2022 - 2024 — McMap. All rights reserved.