On 28.06.15 06.15, Alf P. Steinbach wrote:
>>> virtual ~A() {}
>>>
>>> in class A, then compile and run.
>>>
>>> I'm a big fan of checking reality. ;-)
>>
>> Your test is not valid, as it introduces the vtable into A rather than
>> Bv.
>
> Well, your argument was that
>
> <quote>
> as soon as you have at least one virtual function it makes no difference
> in size anymore
> </quote>
>
> And that's now proved incorrect. The example is very valid for that.
> I.e. for its purpose.
Yes there are constraints. The virtual method must belong to the same
block of memory. I.e. to the same class or to a non virtual base. There
is an additional hack to combine the vtable pointers of a class with its
base class. But this only works for single inheritance.
So as long as A is non virtual it makes no difference whether B has a
virtual method or derives virtual or both. But as soon as A has also
virtual methods (or derives virtual) then the size increases by one
machine size word. Well, except for the case Bvnv where the virtual
tables of A and Bvnv can be combined.
struct A
{
int data;
//virtual ~A() {}
};
struct Bnv: public A
{
int data2;
};
struct Bv: virtual A
{
int data2;
};
struct Bvnv: A
{
int data2;
virtual ~Bvnv() {}
};
struct Bvv: virtual A
{
int data2;
virtual ~Bvv() {}
};
#include <iostream>
int main()
{
using namespace std;
cout << sizeof( Bnv ) << " " << sizeof( Bv ) << " " << sizeof(
Bvnv ) << " " << sizeof( Bvv ) << endl;
}
> But nothing prevents the compiler from introducing a vtable pointer in
> each derived class. That would prevent overhead in member function
> calls.
No, there is no overhead when joining vtables of base and derived. The
vtable of the derived class simply contains the vtable of the base at
the start. There is no additional indirection.
> And this vtable pointer could be used to encode the offset.
Of course, the offsets could be stored in the vtable. And well, this is
an additional indirection since first the vtable address has to be
loaded and then the offset. However, the same applies for any virtual
function call as well. But inlining the vtable in every object also has
drawbacks. First it increases the memory footprint, but now the same
values are read from different memory locations. This causes the memory
cache efficiency to decrease.
In case of virtual base classes there is another difference. Why should
one store the instance independent offsets in the class instance?
Instead the pointers to the virtual base could be stored directly. This
saves one integer addition - probably zero or one clock cycle on
nowadays CPUs.
> On the third hand, the reported sizes indicate that neither MingW g++
> nor Visual C++ do that.
I would not be that sure until I had a look to the assembler output.
Maybe it is not that easy.
>>> Explanation why the vtable is (sually not used to store the offsets:
>> [...]
>>
>> Obviously gcc on OS/2 didn't know that.
>
> Uhm, a single example that "usually" is not "all" isn't valuable in
> itself.
A Raspberry Pi (ARMhf) and LM17 (x86) and Debian Wheezy (x64) show the
same results. But you are right, all of them use some flavor of gcc. So
I guess all gcc versions do it the same way. Tested with different
versions between 3.2.2 and 4.9.2.
But you are right. Other compilers handle it differently. I.e. IBM VAC++
allocates 20 bytes rather than 12 (gcc, 32 bit) for the last test case.
But this one is more than 15 years old.
> [Sorry, I inadvertently first hit "Reply" instead of "Follow up"]
Since you are not in the white list for this address, I did't notice.
Marcel