memory layout of a multiple-inherited object in C++

2.1k views Asked by At
class B1 {
    virtual void f1();
    int int_in_b1;
};
class B2 {
    virtual void f2();
    int int_in_b2;
};
class D: B1, B2 {
    int int_in_d;
    void f1();
    void f2();
};
class D1: B1, B2 {
    int int_in_d;
    virtual void f1();
    virtual void f2();
};

Based on this article, the memory layout for an object d of class D is like this:

d:
  +0: pointer to virtual method table of D (for B1)
  +4: value of int_in_b1
  +8: pointer to virtual method table of D (for B2)
 +12: value of int_in_b2
 +16: value of int_in_d

virtual method table of D (for B1):
  +0: D::f1()  // B1::f1() is overridden by D::f1()

virtual method table of D (for B2):
  +0: D::f2()   // B2::f2() is overridden by D::f2()

What about an object of class D1? In class D1, the members f1 and f2 are both declared as virtual!

2

There are 2 answers

0
jxh On BEST ANSWER

The use of virtual is redundant in D1.

From C++11, §10.3¶2:

If a virtual member function vf is declared in a class Base and in a class Derived, derived directly or indirectly from Base, a member function vf with the same name, parameter-type-list (8.3.5), cv-qualification, and ref-qualifier (or absence of same) as Base::vf is declared, then Derived::vf is also virtual (whether or not it is so declared) and it overrides111 Base::vf.


111) A function with the same name but a different parameter list (Clause 13) as a virtual function is not necessarily virtual and does not override. The use of the virtual specifier in the declaration of an overriding function is legal but redundant (has empty semantics). Access control (Clause 11) is not considered in determining overriding.

Thus, the memory layout (which is what the question seems to be about) is the same for D and D1. Obviously, different types will have different virtual tables.

5
lisyarus On

f1() and f2() are virtual in D too. If a function is declared virtual, it remains virtual in any class that inherits.

So, classes D and D1 almost do not differ inside (but, obviously, are different types), but both of them give implementations of f1 and f2, and their virtual tables will differ, as virtual table for D will point to D's implementations, and the same for D1.

vtable for D:
    f1: D::f1
    f2: D::f2

vtavle for D1:
    f1: D1::f1
    f2: D1::f2