Public Virtual Function Derived Private in C++

Public virtual function derived private in C++

The behavior is correct. Whenever you declare your function as "virtual", you instruct the compiler to generate a virtual call, instead of the direct call to this function. Whenever you override the virtual function in the descendant class, you specify the behavior of this function (you do not change the access mode for those clients, who rely on the "parent's" interface).

Changing the access mode for the virtual function in the descendant class means that you want to hide it from those clients, who use the descendant class directly (who rely on the "child's" interface).

Consider the example:

void process(const A* object) {
object->func();
}

"process" function relies on the parent's interface. It is expected to work for any class, public-derived from A. You cannot public-derive B from A (saying "every B is A"), but hide a part of its interface. Those, who expect "A" must receive a fully functional "A".

Why private virtual member function of a derived class is accessible from a base class

First, as @Eljay notes - printImpl() is a method, albeit virtual, of the Base class. So, it's accessible from the base class. Derived merely provides a different implementation of it. And the whole point of virtual functions is that you can call a subclass' override using a base class reference or pointer.

In other words, private only regards access by subclasses; it's meaningless to keep something private from a class' base class: If a method is at all known to the base class, it must be a method of the base class... a virtual method.

Having said all that - note that the Derived version of printImpl() is effectively inaccessible from print() - when it's invoked within the base class constructor. This is because during that call, the constructed vtable is only that of Base, so printImpl points to Base::printImpl.

I thought private member functions could be called ONLY from the member functions of the same class

And indeed, print() is a member of Base, which invokes printImpl() - another method of Base.

If a private virtual function is overridden as a public function in the derived class, what are the problems?

Accessibility check is performed based on the static type of the object. The type of o is One*. This means that if One::func() is private, then o->func() won't compile.

On the other hand, which virtual member function will be called (i.e. dynamic dispatch) happens at run-time, based on the dynamic type of the object. So if One::func() is public, o->func() will call Two::func(), because o is pointing to an object of type Two.

For your sample code and use case, making One::func() private is just meaningless. But note that there's a famous idiom called Non-Virtual Interface, which makes use of private virtual member functions of base class.


Other suggestions:

  1. Don't forget to delete o;
  2. Add a virtual destructor in the base class One. Otherwise delete o; will lead to undefined behavior; e.g. the destructor of Two might not be invoked.

    class One {
    public:
    virtual ~One() {}
    // ...
    };

C++ override private pure virtual method as public

According to https://en.cppreference.com/w/cpp/language/virtual#In_detail overriding a base's virtual member function only care about the function name, parameters, const/volatile-ness and ref qualifier. It doesn't care about return type, access modifier or other things you might expect it to care about.

The linked reference also specifically notes that :

Base::vf does not need to be visible (can be declared private, or inherited using private inheritance) to be overridden.

Nothing that I can find explicitly gives permission to do this, but the rules of overriding do not prevent it. It's allowed by virtue of virtual functions and function overriding existing and not disallowing this case.

If you are asking why this is how the language is, you may have to ask the standardization committee.

Override public virtual function with private base function?

Is there any way to make this work as I supposed it may have worked?

You should override the member function and explicitly call B::start():

class C: public A, private B {
public:
void start() override { B::start(); }
};

Why would the compiler accept this code as valid? As I see it there
are now two start() functions with the exact same signature in C and
yet the compiler seems fine with it and only calls A::start().

You are right, there are two member functions accessible in C (A::start() and B::start()). And in class C, without overriding start() or making the start() of any of the base classes visible by doing a using ...::start(), you will have ambiguity error when trying to call the member function using unqalified namelookup from an object of C.

class A {
public:
virtual void start() { std::cout << "From A\n"; }
};

class B {
public:
void start() { std::cout << "From B\n"; }
};

class C: public A, private B {
};

int main(){
A* a = new C();
a->start(); //Ok, calls A::start()

C* c = new C();
c->start(); //Error, ambiguous
}

To fix that, you will have to use the qualified name such as:

    C* c = new C();
c->A::start(); //Ok, calls A::start()

Now, doing a using B::start() in class C simply declares the start() to refer to B::start() whenever such name is used from an object of C

class A {
public:
virtual void start() { std::cout << "From A\n"; }
};

class B {
public:
void start() { std::cout << "From B\n"; }
};

class C: public A, private B {
public:
using B::start();
};

int main(){
A* a = new C();
a->start(); //Ok, calls A::start()

C* c = new C();
c->start(); //Ok, calls B::start()
}

using B::start makes the function void B::start() visible in C, it does not override it. To call make all the above unqualified member function call, to call B::start(), you should override the member function in C, and make it call B::start()

class A {
public:
virtual void start() { std::cout << "From A\n"; }
};

class B {
public:
void start() { std::cout << "From B\n"; }
};

class C: public A, private B {
public:
void start() override { B::start(); }
};

int main(){
A* a = new C();
a->start(); //Ok, calls C::start() which in turn calls B::start()
// ^^^^^^^^^^^^^^^^ - by virtual dispatch

C* c = new C();
c->start(); //Ok, calls C::start() which in turn calls B::start()

}


Related Topics



Leave a reply



Submit