Search code examples
c++inheritancereferencevirtual-functionsaccess-specifier

Why virtual functions defy access specifiers ? C++


let's assume u have a class base and class A which inherits from base . base have a declaration of a pure virtual functions called getValue() which is public , and A contains the definition(implementation) of the functions which is set as private . When trying to use the function getValue() from base reference or pointer (base& /base*) to an A object it access it even though it's declared as private


Solution

  • Because in C++, virtuality and access are orthogonal concerns. When the compiler sees a base* or base& and it needs to call getValue on it, then it's sufficient that the function is accessible in base.

    The fact that A declares its (overriding) getValue as private is irrelevant. After all, how could it be relevant? When base.getValue() or base->getValue() is called, you don't know that you might be dealing with an A. That's the whole point of object-oriented programming in the first place!


    This does not mean that it's good style to vary access specifiers within a class hierarchy, though, because it can be confusing. In fact, you should split your getValue into two different functions, one being virtual and the other one non-virtual.

    Long story: C++ behaves very different from other popular programming languages here, because it actually allows and encourages private virtual functions. Virtual member functions should be private by default, and public member functions should be non-virtual by default, and call the private virtual ones if necessary. (The only exception is the destructor, of course.) Herb Sutter once called this the Non-Virtual Interface Idiom.

    Example:

    #include <iostream>
    
    class Base
    {
    public:
        virtual ~Base() {}
    
        int getValue() const
        {
            int const value = doGetValue();
            if (value < 0)
            {
                // error
            }
            return value;
        }
    
    private:
        virtual int doGetValue() const = 0;
    };
    
    class A : public Base
    {
    private:
        int doGetValue() const override
        {
            return 123;
        }
    };
    
    int main()
    {
        Base* ptr = new A; // use std::unique_ptr in real code
        std::cout << ptr->getValue() << "\n";
        delete ptr;
    }