Can this change in subclass require recompilation of code dependent on superclass? - c++

I have been learning some more "indepth" things about virtual tables recently and this question came to my mind.
Suppose we have this sample:
class A {
virtual void foo();
}
class B : public A {
void foo();
}
In this case from what I know there will be a vtable present for each class and the dispatch would be quite simple.
Now suppose we change the B class to something like this:
class B : public C, public A {
void foo();
}
If the class C has some virtual methods the dispatch mechanism for B will be more complicated. There will probably be 2 vtables for both inheritance paths B-C, B-A etc.
From what I've learned so far it seems that if there would be somewhere else in the codebase function like this:
void bar(A * a) {
a->foo();
}
It would need to compile now with the more complicated dispatch mechanism because at compile time we do not know if "a" is pointer to A or B.
Now to the question. Suppose we added the new class B to our codebase. It doesn't seem likely to me that it would require to recompile the code everywhere where the pointer to A is used.
From what I know the vtables are created by the compiler. However is it possible that this fixup is solved by the linker possibly during relocation? It does seem likely to me I just can not find any evidence to be sure and therefore go to sleep right now :)

Inside void bar(A * a), the pointer is definitely to an A object. That A object may be a subobject of something else like a B, but that's irrelevant. The A is self-contained and has its own vtable pointer which links to foo.
When the conversion from B * to A * occurs, such as when bar is called with a B *, a constant offset may be added to the B * to make it point to the A subobject. If the first thing in every object is the vtable, then this will also set the pointer to the A vtable as well. For single inheritance, the adjustment is unnecessary.
Here is what memory looks like for a typical implementation:
| ptr to B vt | members of C | members of B | ptr to AB vt | members of A |
B vt: | ptrs to methods of C (with B overrides) | ptrs to methods of B |
AB vt: | ptrs to methods of A (with B overrides) |
(Note that typically the AB vt is really still part of the B vt; they would be contiguous in memory. And ptrs to methods of B could then go after ptrs to methods of A. I just wrote it this way for formatting clarity.)
When you convert a B * to an A *, you go from this:
| ptr to B vt | members of C | members of B | ptr to AB vt | members of A |
^ your B * pointer value
to this:
| ptr to B vt | members of C | members of B | ptr to AB vt | members of A |
^ your A * pointer value
Using a static_cast from A * to B * will move the pointer backwards, in the other direction.

No, there's no need to recompile the code that depends only on A.
This is actually immediately follows from the principle of "independent translation", which typical modern C++ compilers adhere to. You can write all code dependent only on A in such a way, that it will never include any definitions that mention B. That means that any changes in B will not trigger the recompilation of any A-specific code. That in turn means that a C++ compiler that follows the principle of "independent translation" has to implement simple inheritance, multiple inheritance, virtual dispatch, hierarchical conversions etc. in such a way that any changes in B-specific code do not require recompilation of any A-specific code.
If that wasn't the case, the architecture of a typical C++ compiler would have to be significantly different.

Related

understanding of multiple inheritance for c++

I'm reading multiple inheritance for c++
An Example in the paper:(page 377)
class A {virtual void f();};
class B {virtual void f(); virtual void g();};
class C: A, B {void f();};
A* pa = new C;
B* pb = new C;
C* pc = new C;
pa->f();
pb->f();
pc->f();
pc->g()
(1) Bjarne wrote: On entry to C::f, the this pointer must point to the beginning of the C object (and not to the B part). However, it is not in general known at compile time that the B pointed to by pb is part of a C so the compiler cannot subtract the constant delta(B). So we have to store the delta(B) for the runtime which is actually stored with the vtbl. So the vtbl entry now looks like:
struct vtbl_entry {
void (*fct)();
int delta;
}
An object of class c will look like this:
---------- vtbl:
vptr -------------->-----------------------
A part C::f | 0
---------- -----------------------
vptr -------------->-----------------------
B part C::f | -delta(B)
---------- B::g | 0
C part -----------------------
----------
Bjarne wrote:
pb->f() // call of C::f:
register vtbl_entry* vt = &pb->vtbl[index(f)];
(*vt->fct)((B*)((char*)pb+vt->delta)) //vt->delta is a negative number I guess
I'm totally confused here. Why (B*) not a (C*) in (*vt->fct)((B*)((char*)pb+vt->delta))???? Based on my understanding and Bjarne's introduction at the first sentence at 5.1 section at 377 page, we should pass a C* as this here!!!!!!
Followed by the above code snippet, Bjarne continued writing:
Note that the object pointer may have to be adjusted to po
int to the correct sub-object before looking for the member pointing to the vtbl.
Oh, Man!!! I totally have no idea of what Bjarne tried to say? Can you help me explain it?
It is a C*, it's just not typed as such.
Frankly, that's a pretty terrible explanation and not really how it's done. It's a lot better and easier to store function pointers in the vtable.
struct vtbl {
void(*f)(B* b);
};
struct B {
vtbl* vtable;
};
// Invoke function:
B* p = init();
p->vtable->f(p);
// Function pointer points to:
void f_thunk(B* b) {
C* c = (char*)b - delta(B);
C::f(c);
}
When the compiler generates the thunks, it knows the derived object they're thunking to, so they don't need to store the offset in the vtable. The compiler can simply offset the pointer inside the thunk and then delegate to the appropriate method with the pointer. Of course, this thunk is pretty much generated assembly only without any C++ representation, so it would be invalid to state that that the pointers within have any particular C++ type.
You're getting lost in the weeds here.
Are you trying to write your own C++ compiler? If so, feel free to ignore me. But if you're just trying to understand and learn C++ virtual inheritance, which is what it sounds like, none of what you wrote matters much.
Only compiler writers need to fully understand and figure out how the vtbl works, in all the gory details, in order to actually implement C++. It is not needed to effectively program and develop in C++. All that needs to be understood is how virtual inheritance works, from purely the class's viewpoint. As long as you understand that when invoking pb's method you're actually ending up invoking C's method, and why (with the why being simply "because it is actually an instance of C), that's pretty much all that needs to be understood.
Oh, and your class should probably have a virtual destructor, but that's a different story.
The vtbl is typically not even accessible by C++ code. And the C++ standard does not even require a C++ compiler to actually implement anything that's called "vtable". The only requirement is a specification for how virtual inheritance, and virtual method calls must work. Any actual implementation that produces the same results is compliant.
I'm totally confused here. Why (B*) not a (C*) in (*vt->fct)
At that level, the only known type is B. The actual object could be of type C, Foo, or Bar.
However, that paper is a bit dated. Actual implementations in modern compiler could be very different. #Puppy's answer shows how it can be done without adding delta(B) to the vtable.

Is vptr ever located not at start of object?

According to MSDN, __RTDynamicCast() function is used to implement dynamic_cast in Visual C++. One of its parameters is LONG VfDelta that is described as "offset of virtual function pointer in object".
AFAIK the vptr is always located at start of object, so offset will always be zero. I've looked closely at disassembly of various code snippets using dynamic_cast and I've never seen anything but zero being passed in place of this parameter.
Is vptr ever located anywhere but the object start? Can this offset be anything but zero?
In case of multiple inheritance there are more then one vptr and you need the offset. Take a look here: http://hacksoflife.blogspot.com/2007/02/c-objects-part-3-multiple-inheritance.html
I do not know what Microsoft does, but it's not always true that the vtable pointer is located at offset zero. An example of cases where it may not be is for multiple inheritance (especially if virtual base classes are involved).
Edit:
I'll expand this a bit with examples.
If the first base or a class does not have a vtbl, the derived class will not have a vtbl pointer at offset 0 (such inheritance is bad practice, but is permitted by the language).
If there is a virtual base, the derived class will generally have a pointer to the virtual base at offset 0, not a vtbl pointer.
This functionality is used when virtual inheritance exits ( think about the diamond inheritance chart ). This offset is the offset of the class itself inside the object.
If B and C derives from A, and D derives from both.
A
/ \
B C
\ /
D
Then B and C could be in either order in D. This is where the offset comes into action. So when you dynamic_cast an object of type A to type B, it might be different depending on wether the instance is of type B or D.
Finally to illustrate, here is possible layout of different class
Class B: Class C: class D:
| A | | A | | A |
| B | | C | | C |
| B |
| D |
In this case the offset of virtual function table of B can be either in 0 ( B instance case ), or sizeof( A ) + sizeof( C ) ( D instance case )

C++ - sizeof function applied to object class

I received the following question which was related to sizeof() :
class C
{
public:
C();
virtual ~C();
unsigned char _member0 s[4];
static long _member1 d;
}
int main()
{
C vc;
cout << sizeof(vc);
}
Can someone explain how the sizeof() function is evaluated in this case?
The exact answer could vary from compiler to compiler so in strict sense the answer to your question is this is Implementation Defined.
Considering this to be an interview Q(saw your previous Q), You should have pointed out the following points:
A compiler is allowed to add padding bytes to a structure/class,this might add to the size.
A compiler might add vptr to an class instance,this might add to the size.
The class members will occupy memory.
static members do not contribute towards the size of an class object because they do not belong to an instance of class but to the class.
It gives the size of vc. vc is of class C. Each object of class C contains metadata (pointer to vtable), since C contains virtual methods. In addition, C has a data field (the character array).
Hence the size of vc should be the size of a pointer plus four bytes (plus padding, see comment below, thanks).
d is not component of an object of class C, since it is static, hence it does not count.
So we have:
------vc--------- ----vtable for C---- ----statics----
| ptr to vtable | ----------> | pointer to ~C | | C::d |
|---------------| | ... | | ... |
| char [4] | -------------------- ---------------
-----------------

Mismatch of 'this' address when base class is not polymorphic but derived is

There is this code:
#include <iostream>
class Base
{
public:
Base() {
std::cout << "Base: " << this << std::endl;
}
int x;
int y;
int z;
};
class Derived : Base
{
public:
Derived() {
std::cout << "Derived: " << this << std::endl;
}
void fun(){}
};
int main() {
Derived d;
return 0;
}
The output:
Base: 0xbfdb81d4
Derived: 0xbfdb81d4
However when function 'fun' is changed to virtual in Derived class:
virtual void fun(){} // changed in Derived
Then address of 'this' is not the same in both constructors:
Base: 0xbf93d6a4
Derived: 0xbf93d6a0
The other thing is if class Base is polymorphic, for example I added there some other virtual function:
virtual void funOther(){} // added to Base
then addresses of both 'this' match again:
Base: 0xbfcceda0
Derived: 0xbfcceda0
The question is - why 'this' address is different in Base and Derived class when Base class is not polymorphic and Derived class is?
When you have a polymorphic single-inheritance hierarchy of classes, the typical convention followed by most (if not all) compilers is that each object in that hierarchy has to begin with a VMT pointer (a pointer to Virtual Method Table). In such case the VMT pointer is introduced into the object memory layout early: by the root class of the polymorphic hierarchy, while all lower classes simply inherit it and set it to point to their proper VMT. In such case all nested subobjects within any derived object have the same this value. That way by reading a memory location at *this the compiler has immediate access to VMT pointer regardless of the actual subobject type. This is exactly what happens in your last experiment. When you make the root class polymorphic, all this values match.
However, when the base class in the hierarchy is not polymorphic, it does not introduce a VMT pointer. The VMT pointer will be introduced by the very first polymorphic class somewhere lower in the hierarchy. In such case a popular implementational approach is to insert the VMT pointer before the data introduced by the non-polymorphic (upper) part of the hierarchy. This is what you see in your second experiment. The memory layout for Derived looks as follows
+------------------------------------+ <---- `this` value for `Derived` and below
| VMT pointer introduced by Derived |
+------------------------------------+ <---- `this` value for `Base` and above
| Base data |
+------------------------------------+
| Derived data |
+------------------------------------+
Meanwhile, all classes in the non-polymorphic (upper) part of the hierarchy should know nothing about any VMT pointers. Objects of Base type must begin with data field Base::x. At the same time all classes in the polymorphic (lower) part of the hierarchy must begin with VMT pointer. In order to satisfy both of these requirements, the compiler is forced to adjust the object pointer value as it is converted up and down the hierarchy from one nested base subobject to another. That immediately means that pointer conversion across the polymorphic/non-polymorphic boundary is no longer conceptual: the compiler has to add or subtract some offset.
The subobjects from non-polymorphic part of the hierarchy will share their this value, while subobjects from the polymorphic part of hierarchy will share their own, different this value.
Having to add or subtract some offset when converting pointer values along the hierarchy is not unusual: the compiler has to do it all the time when dealing with multiple-inheritance hierarchies. However, you example shows how it can be achieved in single-inheritance hierarchy as well.
The addition/subtraction effect will also be revealed in a pointer conversion
Derived *pd = new Derived;
Base *pb = pd;
// Numerical values of `pb` and `pd` are different if `Base` is non-polymorphic
// and `Derived` is polymorphic
Derived *pd2 = static_cast<Derived *>(pb);
// Numerical values of `pd` and `pd2` are the same
This looks like behavior of a typical implementation of polymorphism with a v-table pointer in the object. The Base class doesn't require such a pointer since it doesn't have any virtual methods. Which saves 4 bytes in the object size on a 32-bit machine. A typical layout is:
+------+------+------+
| x | y | z |
+------+------+------+
^
| this
The Derived class however does require the v-table pointer. Typically stored at offset 0 in the object layout.
+------+------+------+------+
| vptr | x | y | z |
+------+------+------+------+
^
| this
So to make the Base class methods see the same layout of the object, the code generator adds 4 to the this pointer before calling a method of the Base class. The constructor sees:
+------+------+------+------+
| vptr | x | y | z |
+------+------+------+------+
^
| this
Which explains why you see 4 added to the this pointer value in the Base constructor.
Technically speaking, this is exactly what happens.
However it must be noted that according to the language specification, the implementation of polymorphism does not necessarily relate to vtables: this is what the spec. defines as "implementation detail", that is out of the specs scope.
All what we can say is that this has a type, and points to what is accessible through its type.
How the dereferencing into members happens, again, is an implementation detail.
The fact that a pointer to something when converted into a pointer to something else, either by implicit, static or dynamic conversion, has to be changed to accommodate what is around must be considered the rule, not the exception.
By the way C++ is defined, the question is meaningless, as are the answers, since they assume implicitly that the implementation is based on the supposed layouts.
The fact that, under given circumstances, two object sub-components share a same origin, is just a (very common) particular case.
The exception is "reinterpreting": when you "blind" the type system, and just say "look this bunch of bytes as they are an instance of this type": that's the only case you have to expect no address change (and no responsibility from the compiler about the meaningfulness of such a conversion).

understanding vptr in multiple inheritance?

I am trying to make sense of the statement in book effective c++. Following is the inheritance diagram for multiple inheritance.
Now the book says separate memory in each class is required for vptr. Also it makes following statement
An oddity in the above diagram is that there are only three vptrs even though four classes are involved. Implementations are free to generate four vptrs if they like, but three suffice (it turns out that B and D can share a vptr), and most implementations take advantage of this opportunity to reduce the compiler-generated overhead.
I could not see any reason why there is requirement of separate memory in each class for vptr. I had an understanding that vptr is inherited from base class whatever may be the inheritance type. If we assume that it shown resultant memory structure with inherited vptr how can they make the statement that
B and D can share a vptr
Can somebody please clarify a bit about vptr in multiple inheritance?
Do we need separate vptr in each class ?
Also if above is true why B and D can share vptr ?
Your question is interesting, however I fear that you are aiming too big as a first question, so I will answer in several steps, if you don't mind :)
Disclaimer: I am no compiler writer, and though I have certainly studied the subject, my word should be taken with caution. There will me inaccuracies. And I am not that well versed in RTTI. Also, since this is not standard, what I describe are possibilities.
1. How to implement inheritance ?
Note: I will leave out alignment issues, they just mean that some padding could be included between the blocks
Let's leave it out virtual methods, for now, and concentrate on how inheritance is implemented, down below.
The truth is that inheritance and composition share a lot:
struct B { int t; int u; };
struct C { B b; int v; int w; };
struct D: B { int v; int w; };
Are going to look like:
B:
+-----+-----+
| t | u |
+-----+-----+
C:
+-----+-----+-----+-----+
| B | v | w |
+-----+-----+-----+-----+
D:
+-----+-----+-----+-----+
| B | v | w |
+-----+-----+-----+-----+
Shocking isn't it :) ?
This means, however, than multiple inheritance is quite simple to figure out:
struct A { int r; int s; };
struct M: A, B { int v; int w; };
M:
+-----+-----+-----+-----+-----+-----+
| A | B | v | w |
+-----+-----+-----+-----+-----+-----+
Using these diagrams, let's see what happens when casting a derived pointer to a base pointer:
M* pm = new M();
A* pa = pm; // points to the A subpart of M
B* pb = pm; // points to the B subpart of M
Using our previous diagram:
M:
+-----+-----+-----+-----+-----+-----+
| A | B | v | w |
+-----+-----+-----+-----+-----+-----+
^ ^
pm pb
pa
The fact that the address of pb is slightly different from that of pm is handled through pointer arithmetic automatically for you by the compiler.
2. How to implement virtual inheritance ?
Virtual inheritance is tricky: you need to ensure that a single V (for virtual) object will be shared by all the other subobjects. Let's define a simple diamond inheritance.
struct V { int t; };
struct B: virtual V { int u; };
struct C: virtual V { int v; };
struct D: B, C { int w; };
I'll leave out the representation, and concentrate on ensuring that in a D object, both the B and C subparts share the same subobject. How can it be done ?
Remember that a class size should be constant
Remember that when designed, neither B nor C can foresee whether they will be used together or not
The solution that has been found is therefore simple: B and C only reserve space for a pointer to V, and:
if you build a stand-alone B, the constructor will allocate a V on the heap, which will be handled automatically
if you build B as part of a D, the B subpart will expect the D constructor to pass the pointer to the location of V
And idem for C, obviously.
In D, an optimization allow the constructor to reserve space for V right in the object, because D does not inherit virtually from either B or C, giving the diagram you have shown (though we don't have yet virtual methods).
B: (and C is similar)
+-----+-----+
| V* | u |
+-----+-----+
D:
+-----+-----+-----+-----+-----+-----+
| B | C | w | A |
+-----+-----+-----+-----+-----+-----+
Remark now that casting from B to A is slightly trickier than simple pointer arithmetic: you need follow the pointer in B rather than simple pointer arithmetic.
There is a worse case though, up-casting. If I give you a pointer to A how do you know how to get back to B ?
In this case, the magic is performed by dynamic_cast, but this require some support (ie, information) stored somewhere. This is the so called RTTI (Run-Time Type Information). dynamic_cast will first determine that A is part of a D through some magic, then query D's runtime information to know where within D the B subobject is stored.
If we were in case where there is no B subobject, it would either return 0 (pointer form) or throw a bad_cast exception (reference form).
3. How to implement virtual methods ?
In general virtual methods are implemented through a v-table (ie, a table of pointer to functions) per class, and v-ptr to this table per-object. This is not the sole possible implementation, and it has been demonstrated that others could be faster, however it is both simple and with a predictable overhead (both in term of memory and dispatch speed).
If we take a simple base class object, with a virtual method:
struct B { virtual foo(); };
For the computer, there is no such things as member methods, so in fact you have:
struct B { VTable* vptr; };
void Bfoo(B* b);
struct BVTable { RTTI* rtti; void (*foo)(B*); };
When you derive from B:
struct D: B { virtual foo(); virtual bar(); };
You now have two virtual methods, one overrides B::foo, the other is brand new. The computer representation is akin to:
struct D { VTable* vptr; }; // single table, even for two methods
void Dfoo(D* d); void Dbar(D* d);
struct DVTable { RTTI* rtti; void (*foo)(D*); void (*foo)(B*); };
Note how BVTable and DVTable are so similar (since we put foo before bar) ? It's important!
D* d = /**/;
B* b = d; // noop, no needfor arithmetic
b->foo();
Let's translate the call to foo in machine language (somewhat):
// 1. get the vptr
void* vptr = b; // noop, it's stored at the first byte of B
// 2. get the pointer to foo function
void (*foo)(B*) = vptr[1]; // 0 is for RTTI
// 3. apply foo
(*foo)(b);
Those vptrs are initialized by the constructors of the objects, when executing the constructor of D, here is what happened:
D::D() calls B::B() first and foremost, to initiliaze its subparts
B::B() initialize vptr to point to its vtable, then returns
D::D() initialize vptr to point to its vtable, overriding B's
Therefore, vptr here pointed to D's vtable, and thus the foo applied was D's. For B it was completely transparent.
Here B and D share the same vptr!
4. Virtual tables in multi-inheritance
Unfortunately this sharing is not always possible.
First, as we have seen, in the case of virtual inheritance, the "shared" item is positionned oddly in the final complete object. It therefore has its own vptr. That's 1.
Second, in case of multi-inheritance, the first base is aligned with the complete object, but the second base cannot be (they both need space for their data), therefore it cannot share its vptr. That's 2.
Third, the first base is aligned with the complete object, thus offering us the same layout that in the case of simple inheritance (the same optimization opportunity). That's 3.
Quite simple, no ?
If a class has virtual members, one need to way to find their address. Those are collected in a constant table (the vtbl) whose address is stored in an hidden field for each object (vptr). A call to a virtual member is essentially:
obj->_vptr[member_idx](obj, params...);
A derived class which add virtual members to his base class also need a place for them. Thus a new vtbl and a new vptr for them. A call to an inherited virtual member is still
obj->_vptr[member_idx](obj, params...);
and a call to new virtual member is:
obj->_vptr2[member_idx](obj, params...);
If the base is not virtual, one can arrange for the second vtbl to be put immediately after the first one, effectively increasing the size of the vtbl. And the _vptr2 is no more needed. A call to a new virtual member is thus:
obj->_vptr[member_idx+num_inherited_members](obj, params...);
In the case of (non virtual) multiple inheritance, one inherit two vtbl and two vptr. They can't be merged, and calls must pay attention to add an offset to the object (in order for the inherited data members to be found at the correct place). Calls to the first base class members will be
obj->_vptr_base1[member_idx](obj, params...);
and for the second
obj->_vptr_base2[member_idx](obj+offset, params...);
New virtual members can again either be put in a new vtbl, or appended to the vtbl of the first base (so that no offsets are added in future calls).
If a base is virtual, one can not append the new vtbl to the inherited one as it could leads to conflicts (in the example you gave, if both B and C append their virtual functions, how D be able to build its version?).
Thus, A needs a vtbl. B and C need a vtbl and it can't be appended to A's one because A is a virtual base of both. D needs a vtbl but it can be appended to B one as B is not a virtual base class of D.
It all has to do with how compiler figures out the actual addresses of method functions. The compiler assumes that virtual table pointer is located at a known offset from the base of the object (typically at offset 0). The compiler also needs to know the structure of the virtual table for each class - in other words, how to lookup pointers to functions in the virtual table.
Class B and class C will have completely different structures of Virtual Tables since they have different methods. Virtual table for class D can look like a virtual table for class B followed by additional data for methods of class C.
When you generate an object of class D, you can cast it as a pointer to B or as a pointer to C or even as a pointer to class A. You may pass these pointers to modules that are not even aware of existence of class D, but can call methods of class B or C or A. These modules need to know how to locate the pointer to the virtual table of the class and they need to know how to locate pointers to methods of class B/C/A in the virtual table. That's why you need to have separate VPTRs for each class.
Class D is well aware of existence of class B and the structure of its virtual table and therefore can extend its structure and reuse the VPTR from object B.
When you cast a pointer to object D to a pointer to object B or C or A, it will actually update the pointer by some offset, so that it starts from vptr corresponding to that specific base class.
I could not see any reason why there
is requirement of separate memory in
each class for vptr
At runtime, when you invoke a (virtual) method via a pointer, the CPU has no knowledge about the actual object on which the method is dispatched. If you have B* b = ...; b->some_method(); then the variable b can potentially point at an object created via new B() or via new D() or
even new E() where E is some other class that inherits from (either) B or D. Each of these classes can supply its own implementation (override) for some_method(). Thus, the call b->some_method() should dispatch the implementation from either B, D or E depending on the object on which b is pointing.
The vptr of an object allows the CPU to find the address of the implementation of some_method that is in effect for that object. Each class defines it own vtbl (containing addresses of all virtual methods) and each object of the class starts with a vptr that points at that vtbl.
I think D needs 2 or 3 vptrs.
Here A may or may not require a vptr.
B needs one that should not be shared with A (because A is virtually inherited).
C needs one that should not be shared with A (ditto).
D can use B or C's vftable for its new virtual functions (if any), so it can share B's or C's.
My old paper "C++: Under the Hood" explains the Microsoft C++ implementation of virtual base classes. http://www.openrce.org/articles/files/jangrayhood.pdf
And (MS C++) you can compile with cl /d1reportAllClassLayout to get a text report of class memory layouts.
Happy hacking!