Re: Non virtual and inheritance
Sensei <sensei_s_mail_is_at@me.com> wrote in news:k5oqnl$slk$1
@speranza.aioe.org:
I was philosophizing on inheritance in C++, and I was wondering this.
Bear with me, as this is something more of a philosophy/history, rather
than a technical one.
What would be a use case for non virtual members in a class hierarchy?
class Base
{
public:
virtual void hello() { std::cout << "Base" << std::endl; }:
}
class Derived : public Base
{
public:
virtual void hello() { std::cout << "Derived" << std::endl; }:
}
Virtual allows me to use a Base class, and yet call the right function
hello().
Base *b = new Base;
Base *d = new Derived;
b->hello();
d->hello();
Now, if I let virtual out from Base and Derived, I will "slice" the
derived class, and d->hello() will print, "Base".
No slicing here, the object is still intact and you can access it by
using a right cast. A real slicing would look like this:
Base b = *d;
Why is a member function non-virtual by default, even from an
historically accurate point of view?
Zero overhead principle. If you don't use a certain language feature
(like virtual functions) you don't pay any cost (neither in the source
code nor at the run-time) for the things you don't use.
This is especially important for function inlining. A non-virtual
function can be inlined easily if the function definition is visible to
the optimizer; inlining a virtual function is a much more difficult task,
and often impossible.
From a user point of view, the "slicing" is an "error", or an
unexpected behavior, to say the least. Having a base class pointer is
useful, and it will call the right derived class when needed.
Yes, this is what Java is doing IIRC. Java was supposed to be a better
C++. In some sense it is, in some sense not (see below).
Not only that, but choosing non-virtual as the default behavior, means
that virtual is quite "extravagant", and therefore needs to be added by
hand. But, at least in my experience, virtual is a given, and
non-virtual is the exception.
In my experience, virtual happens a lot less than non-virtual. Note that
introducing a virtual function is not a trivial task; when doing this one
needs to not only specify the interface for one class, but for a
potentially unlimited set of classes which might not even exist yet. And
one has to ensure that each of those (possibly not yet existing)
overridden virtual functions behaves properly when the virtual function
is called in all those N places in other code. That's why there is a
pattern in C++ to make all virtual functions private or protected, and
provide only one non-virtual member function which is calling the
virtual. This non-virtual function typically contains a lot of pre-
condition and post-condition checks to leverage the uncertainty of
calling a possibly "wild" piece of code.
So no, a virtual function is everything else than "given".
I'd really like to understand the rationale behind this choice, with a
use case that makes sense from an operational point of view, and not
just as an academic exercise.
This question bugged me when I tried to make only Derived non-virtual,
and subclassing it.
class Base
{
public:
virtual void hello() { std::cout << "Base" << std::endl; };
};
class Derived : public Base
{
public:
void hello() { std::cout << "Derived" << std::endl; };
};
class Last : public Derived
{
public:
void hello() { std::cout << "Last" << std::endl; };
};
At least on my system (Apple clang version 4.1 x86_64-apple-
darwin12.2.0
Thread model: posix), it seems that "virtual" survives:
Base *b = new Base;
Base *d = new Derived;
Base *l = new Last;
b->hello();
d->hello();
l->hello();
% clang++ a.cpp && ./a.out
clang++ a.cpp && ./a.out
Base
Derived
Last
Is this the correct behavior? Why is this?
Yes, this is correct. I would have preferred to have a compiler error for
your code instead, but in this case the C++ language designers have gone
the route of silently "fixing" the code behind the scenes, effectively
adding "virtual" to the derived class function declarations where it is
obviously missing.
Cheers
Paavo