Search code examples
c++classinheritanceprivate

Does a function that takes a pointer to base class also accept a child class that derived privately from the base class?


This came up in my c++ class, let say we have a base class with some public functions.

class basevector {
public:
virtual int indexTo(int arg);
virtual int getSize() const;
virtual int push(int);
virtual int pop(int);
}

then we have a class that derives privately

class myvector : private basevector{
public :  
virtual int push(int);
virtual int pop(int);
virtual int getSize() const;
}

Notice that the myvector class restricts access to the indexTo function and forces the user to use the push and pop.

Now if I have a function that takes a pointer to basevector

void do_something(basevector * arg) {
    for(int i = 0; i < arg->getSize(); i++) {
        int k = arg->indexTo(i);
        do_something_else(k);
    }
}

I then try to do the following

basevector * ptr = nullptr;
ptr = new myvector;

do_something(ptr);

What happens? myvector is a basevector but a function that tries to use the public indexTo function in basevector will fail on a myvector.


Solution

  • Welcome to virtual functions! Whenever you mark a function in a class as virtual you're telling the compiler, "Hey, I might override this function later. If I do, then make sure to use the new overridden version!". It has nothing to do with restricting access to functions within a class. That's what the public keyword does.

    In your specific example, you can access all four functions from both basevector and myvector. However, what those functions represent is dependent on the type of the value that you're using (not the type of the pointer!) When you create a new myvector, you're also creating a table that points to the functions that it should use:

    push    --> myvector::push
    pop     --> myvector::pop
    getSize --> myvector::getSize
    indexTo --> basevector::indexTo
    

    That last one happens automatically because you're inheriting from basevector when you declare myvector. In your definition of myvector though, you've overridden the other three.

    OK. So what?

    Well, when you define a new function that takes a generic basevector, like do_something, the compiler knows that basevector has virtual functions. Whenever it invokes one of these functions, it has to go look in the table that was generated when the basevector was created. In your example, that table is the table from a myvector. Hence, do_something will call basevector's indexTo regardless of whether or not you pass in a basevector or myvector.

    You can actually force myvector to implement its own version of indexTo:

    class basevector {
      public:
        virtual int indexTo(int arg) = 0;
        /* ... */
    };
    

    Now if you try and create a new myvector, the compiler will complain saying that it can't find something to point to for its indexTo entry in it's virtual function table (commonly called a "V-table").

    However, when it comes to private inheritance, you should treat it as the following:

    class A : private B { ... 
    

    this means "Everything that an A has access to in B will be private to people outside of A". So later, you can do things like:

    myvector *p1 = new myvector;
    basevector *p2;
    // p2 = p1;    // <-- Compiler will complain here because we don't know that
                   // basevector is a base class of myvector!
    p2 = reinterpret_cast<basevector *>(p1);
    p2->pop();  // Super hacky but identical to calling p1->pop();
    

    However, as mentioned in this question, using private inheritance hides that basevector is the base class of myvector too! Since we know it is, we can cast the pointer to a basevector and elicit the same behavior because we know how virtual functions work.