Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c++ > #19049 > unrolled thread

Non virtual and inheritance

Started bySensei <sensei_s_mail_is_at@me.com>
First post2012-10-18 13:55 +0200
Last post2012-10-19 09:41 +0000
Articles 9 — 6 participants

Back to article view | Back to comp.lang.c++


Contents

  Non virtual and inheritance Sensei <sensei_s_mail_is_at@me.com> - 2012-10-18 13:55 +0200
    Re: Non virtual and inheritance Nobody <nobody@nowhere.com> - 2012-10-18 13:29 +0100
      Re: Non virtual and inheritance Juha Nieminen <nospam@thanks.invalid> - 2012-10-19 09:39 +0000
        Re: Non virtual and inheritance Nobody <nobody@nowhere.com> - 2012-10-19 15:01 +0100
          Re: Non virtual and inheritance Juha Nieminen <nospam@thanks.invalid> - 2012-10-19 16:01 +0000
    Re: Non virtual and inheritance Rui Maciel <rui.maciel@gmail.com> - 2012-10-18 14:49 +0100
    Re: Non virtual and inheritance Luca Risolia <luca.risolia@studio.unibo.it> - 2012-10-18 20:06 +0200
    Re: Non virtual and inheritance Paavo Helde <myfirstname@osa.pri.ee> - 2012-10-18 14:40 -0500
    Re: Non virtual and inheritance Juha Nieminen <nospam@thanks.invalid> - 2012-10-19 09:41 +0000

#19049 — Non virtual and inheritance

FromSensei <sensei_s_mail_is_at@me.com>
Date2012-10-18 13:55 +0200
SubjectNon virtual and inheritance
Message-ID<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".

Why is a member function non-virtual by default, even from an 
historically accurate point of view?

 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.

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.

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?


Thanks!!


[toc] | [next] | [standalone]


#19050

FromNobody <nobody@nowhere.com>
Date2012-10-18 13:29 +0100
Message-ID<pan.2012.10.18.12.29.32.390000@nowhere.com>
In reply to#19049
On Thu, 18 Oct 2012 13:55:35 +0200, Sensei wrote:

> Why is a member function non-virtual by default, even from an historically
> accurate point of view?

Virtual calls are more computationally expensive, and the presence of any
virtual methods make the class a non-POD type. Virtual calls are pointless
if the class isn't intended to be derived from (as most STL containers
aren't).

[toc] | [prev] | [next] | [standalone]


#19072

FromJuha Nieminen <nospam@thanks.invalid>
Date2012-10-19 09:39 +0000
Message-ID<k5r756$cmd$1@speranza.aioe.org>
In reply to#19050
Nobody <nobody@nowhere.com> wrote:
> Virtual calls are more computationally expensive

Actually if you actually measure the speed difference between calling
a regular (non-inlined) function and a virtual function, the difference
is barely measurable, if not inexistent. Yes, there's an additional
indirection step, but its impact on the overall speed of the function
call is quite minimal (especially if the function takes parameters and
does something that takes many clock cycles).

In short: If you are avoiding virtual functions because you fear that
your program will become slower, then you are worrying for nothing.

The actual problem with virtual functions (which is only a problem in
certain cases) is that a class having virtual functions will be larger
(typically by a pointer) than a class with no virtual functions. In
most cases this doesn't matter, but if you are super-tightly optimizing
the memory usage of a very small class, that size increase can be crucial.
(If eg. your class only has eg. an integral as a member, making any of the
functions virtual will double the size of the class.)

(Virtuality may also impede or reduce the effectiveness of function
inlining, but that's also something that is relevant only in certain
cases.)

This overhead is useless if you don't need virtual functions for anything.

[toc] | [prev] | [next] | [standalone]


#19087

FromNobody <nobody@nowhere.com>
Date2012-10-19 15:01 +0100
Message-ID<pan.2012.10.19.14.01.58.787000@nowhere.com>
In reply to#19072
On Fri, 19 Oct 2012 09:39:50 +0000, Juha Nieminen wrote:

> Nobody <nobody@nowhere.com> wrote:
>> Virtual calls are more computationally expensive
> 
> Actually if you actually measure the speed difference between calling
> a regular (non-inlined) function and a virtual function, the difference
> is barely measurable, if not inexistent. Yes, there's an additional
> indirection step, but its impact on the overall speed of the function
> call is quite minimal (especially if the function takes parameters and
> does something that takes many clock cycles).

It wasn't always thus. CPUs were rather more naive when C++ was originally
designed. But the biggest issue is that virtual typically inhibits
inlining, which has an impact far beyond the cost of a function call.

> In short: If you are avoiding virtual functions because you fear that
> your program will become slower, then you are worrying for nothing.
> 
> The actual problem with virtual functions (which is only a problem in
> certain cases) is that a class having virtual functions will be larger
> (typically by a pointer) than a class with no virtual functions.

If the vptr is a problem, it isn't usually due to the size, but to making
the type non-POD. E.g. being unable to pass such objects between processes
using shared memory, or efficient I/O via mmap(), or making the memory
layout conform to a pre-defined format.

[toc] | [prev] | [next] | [standalone]


#19092

FromJuha Nieminen <nospam@thanks.invalid>
Date2012-10-19 16:01 +0000
Message-ID<k5rtgl$72q$1@speranza.aioe.org>
In reply to#19087
Nobody <nobody@nowhere.com> wrote:
> If the vptr is a problem, it isn't usually due to the size, but to making
> the type non-POD.

It depends on the application, but it often is a question of size.
If you need to optimize the size of a class (usually because you need
to make millions of instantiations, and you need for them to be as fast
as possible and take as little memory as possible), even one additional
pointer may significantly increase memory usage.

I'd say the size issue is (at least in my experience) significantly
more prevalent than the non-POD issue.

[toc] | [prev] | [next] | [standalone]


#19051

FromRui Maciel <rui.maciel@gmail.com>
Date2012-10-18 14:49 +0100
Message-ID<k5p1di$evk$1@speranza.aioe.org>
In reply to#19049
Sensei wrote:

> 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".
> 
> Why is a member function non-virtual by default, even from an
> historically accurate point of view?

See the answer provided by Nobody.


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

No.  It may go against your perception of how virtual functions work, but it 
is far from an error.  If you define void Base::hello() and when you call 
the member function hello() of an object of type Base , you cannot be 
surprized if, by doing so, you end up calling void Base::hello().  The 
program is running exactly as you set it to run.


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

Then declare the member functions of your base class as virtual.  If you 
declare a virtual member function in a base class then whenever you define 
the same member function in a derived class, that member function will be 
virtual whether you explicitly define it as virtual or not.


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

<source lang="cpp">
#include <iostream>


struct Base
{
        void foo() { } 
};

struct DerivedA: public Base
{
        virtual void foo() { } 
};

struct DerivedB: public Base
{
        void foo() { } 
};



int main(void)
{
        using namespace std;
        cout << "Base size: " << sizeof(Base) << "\n";
        cout << "DerivedA size: " << sizeof(DerivedA) << "\n";
        cout << "DerivedB size: " << sizeof(DerivedB) << endl;
        return 0;
}
</source>

<shell>
rui@kubuntu:tmp$ g++ main.c++ 
rui@kubuntu:tmp$ ./a.out 
Base size: 1
DerivedA size: 8
DerivedB size: 1
</shell>


> 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.  You've declared Base::hello() as virtual.  Hence, every* time you 
declare that member function in a class that directly or indirectly derives 
from Base, the member function defined in the derived class will also be 
virtual.


Rui Maciel


* The "every time" bit may not be entirely correct in C++11, with the 
introduction of "final" and "override".  Someone please chime in to clarify 
this bit.

[toc] | [prev] | [next] | [standalone]


#19057

FromLuca Risolia <luca.risolia@studio.unibo.it>
Date2012-10-18 20:06 +0200
Message-ID<k5pgfo$5fk$1@speranza.aioe.org>
In reply to#19049
On 18/10/2012 13:55, Sensei wrote:
> 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.

Note that C++11 introduces virtual specifiers: new, final,explicit, 
override to make the kind of inheritance more clear.

[toc] | [prev] | [next] | [standalone]


#19059

FromPaavo Helde <myfirstname@osa.pri.ee>
Date2012-10-18 14:40 -0500
Message-ID<XnsA0F0E6BEA972Bmyfirstnameosapriee@216.196.109.131>
In reply to#19049
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

[toc] | [prev] | [next] | [standalone]


#19074

FromJuha Nieminen <nospam@thanks.invalid>
Date2012-10-19 09:41 +0000
Message-ID<k5r794$cmd$2@speranza.aioe.org>
In reply to#19049
Sensei <sensei_s_mail_is_at@me.com> wrote:
> Why is a member function non-virtual by default, even from an 
> historically accurate point of view?

The design principle of C++ has always been "you don't pay for what
you don't use."

A class having virtual functions is larger than a class with no virtual
functions, and in some cases that might matter. (Also, virtuality may
impede or reduce the effectiveness of function inlining.)

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.c++


csiph-web