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