1

I am working on a class (call it class D) that is derived from a class (call it class B) and will be inherited by another class (call it class E). I want instances of class E to have access to all the public functions of its parents but I specifically want to prevent class E from overriding any of the virtual functions in parent classes.

I thought by overriding the function in the private part of class D it would prevent the derived classes from overriding, but that appears not to be the case. This compiles and runs the function in class E:

#include <iostream>

//an abstract base class
class b
{
public:
    virtual ~b() = default;
protected:
    virtual void print() = 0;
    virtual void print_two()
    {
        std::cout << "base class" << std::endl;
    }
};

class d: public b
{
public:
    virtual ~d() = default;
private:
    void print() override // note this is not virtual -adding final here does the trick
    {
        std::cout << "class d" << std::endl;
    }
    using b::print_two; //this doesn't work either
};

class e : public d
{
public:
    void print() override //why is this possible given the function it's overriding is private and non-virtual?
    {
        std::cout << "class e" << std::endl;
    }

    void print_two() override //why is this possible given the function it's overriding is private and non-virtual?
    {
        std::cout << "print two class e" << std::endl;
    }
};


int main()
{
    e foo;
    foo.print();
    foo.print_two();

}

So two questions out of this: Why can I override a function that is virtual in a grandparent but not virtual in a parent? Why can I override a function that is protected in a grandparent but private in a parent?

I tried it in g++ and clang and it compiles with no errors or warnings (-Wall -Wextra -Wpedantic)

schrödinbug
  • 692
  • 9
  • 19
  • In `d`, try this: `virtual void print() override final { ... }` – Eljay Feb 13 '18 at 19:23
  • "*note this is not virtual*" It actually *is* virtual, so one of your premises is wrong. (if it wasn't virtual you wouldn't be able to `override` it.) – juanchopanza Feb 13 '18 at 19:23
  • Once a function is marked `virtual` it's *sticky*. It stays with the function in all child-classes. There's no way to *un-virtual* a function. – Some programmer dude Feb 13 '18 at 19:23
  • Also, virtual has nothing to do with access level. Overriding private stuff can be quite useful (see template method pattern.) – juanchopanza Feb 13 '18 at 19:24
  • Possible duplicate of [Private virtual method in C++](https://stackoverflow.com/questions/2170688/private-virtual-method-in-c) –  Feb 13 '18 at 19:24
  • If you don't want a virtual function to be overridden by deriving classes, then mark it as [final](http://en.cppreference.com/w/cpp/language/final). – Jesper Juhl Feb 13 '18 at 19:29
  • /OT _Schrödingbug_ is new to me, I just knew _Heisenbug_ so far. Is that commented dead code, that is reactivated by some cat tapping at the keyboard and changing and submitting it by rare chance? –  Feb 13 '18 at 19:29
  • You should look up Bohrbug, Mandelbug, and hindenbug https://en.wikipedia.org/wiki/Heisenbug – schrödinbug Feb 13 '18 at 19:41
  • @Eljay is it possible to mark a function final in a derived class that is not actually overriden in that class (such as the print_two() function)? – schrödinbug Feb 13 '18 at 19:46
  • @schrödinbug • not that I'm aware of. – Eljay Feb 13 '18 at 21:02

1 Answers1

7

Why can I override a function that is virtual in a grandparent but not virtual in a parent?

There's no such thing. Once a function is declared virtual, it's implicitly virtual in all derived classes. Only way to come close to "unvirtualize" a member function is to declare it final. That would make it impossible to override it further, but it will still be subject to dynamic dispatch when called via an ancestor reference/pointer. Any access via the child that declared it final or its decedents, could be resolved statically, however.

Why can I override a function that is protected in a grandparent but private in a parent?

Access specifiers don't affect a name's visibility. They only affect where that name can be used. Defining the same function will override the parent's implementation and be called by dynamic dispatch. Again, because this is not something that falls under the realms of "access" to the name.

The child can't refer to the parent's implementation (Parent::foo) if it's private in the parent, and neither can outside code that consumes the interface. It ensure the function is only ever called in a controlled manner. Very useful for testing pre and post conditions.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • Was this one of the tricks to bypass visibility check and call private functions? – Incomputable Feb 13 '18 at 19:25
  • @Incomputable - What do you mean? – StoryTeller - Unslander Monica Feb 13 '18 at 19:26
  • I’ve heard that private functions can still be callable directly outside of the class. My initial guess was ADL tricks, but after reading this I got stronger candidate. May be I was confused. – Incomputable Feb 13 '18 at 19:28
  • @Incomputable - You aren't confused. A deriving class can place whatever access specifier it wants on its own implementation. But the parent implementation is locked as private. So it's only half a bypass. – StoryTeller - Unslander Monica Feb 13 '18 at 19:31
  • @Incomputable - [The standard has an example](http://eel.is/c++draft/class.access.virt). It's also possible to make it public in the child. I'm guessing the advise you read pertains to that case. Where a child thinks its defining a private function, when its only a cast a way from being called externally. – StoryTeller - Unslander Monica Feb 13 '18 at 19:32
  • @StoryTeller Access vs. Visibility. Makes sense. So if you can't unmake a function virtual, why do you have to mark the destructor virtual in each derived class or get warnings from the compiler? https://stackoverflow.com/a/48667887/2748602 – schrödinbug Feb 13 '18 at 19:40
  • @schrödinbug - Not each derived class. Only at the root of the heirarchy from which you intend to delete polymorphically. The compiler is issuing a warning to a common pitfall. Making a destructor virtual *once* is enough. In the post you linked, the asker doesn't mark any destructor as virtual. Not in the base class, nor in the derived one. – StoryTeller - Unslander Monica Feb 13 '18 at 19:43
  • @StoryTeller is it possible to mark a function final in a derived class that is not actually overriden in that class (such as the print_two() function)? – schrödinbug Feb 13 '18 at 19:58
  • @schrödinbug - Not according to the C++ standard as it is written today. – StoryTeller - Unslander Monica Feb 13 '18 at 20:09
  • @StoryTeller I guess the only way is to override the function and declare it final, but just have it call the parent classes' function. i.e. instead of the using statement in class d, replace it with `void print_two() override final {b::print_two();}` – schrödinbug Feb 13 '18 at 20:16
  • @schrödinbug - Yes. It's also worth noting, that if `b::print_two` was private, you wouldn't be able to do that. – StoryTeller - Unslander Monica Feb 13 '18 at 20:19