c++inheritancevirtual-functionsprivate-membersmember-access

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


Consider the following snippet of code:

#include <iostream>
    
class Base {
public:
  Base() {
      std::cout << "Base::constr" << std::endl;
      print();
  }
  virtual ~Base() = default;

  void print() const { printImpl(); }
  
private:
    virtual void printImpl() const {
        std::cout << "Base::printImpl" << std::endl;
    }
};

class Derived : public Base {
public:        
    Derived() {
        std::cout << "Derived::constr" << std::endl;
    }
    
private:
    void printImpl() const override {
        std::cout << "Derived::printImpl" << std::endl;
    }
};

int main() {
    Base* ptr = new Derived();
    ptr->print();
    delete ptr;
}

The above code will print the following:

Base::constr
Base::printImpl
Derived::constr
Derived::printImpl

but I don't understand why printImpl private function is accessible from the base's print function. In my understanding this pointer implicitly passed to the print function holds the derived object's address, but I thought private member functions could be called ONLY from the member functions (and from friend functions) of the same class and here, Base class is not the same class as Derived, although there is an is a relationship.


Solution

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