Multiple inheritance and polymorphism in C++ - c++

Consider the following code:
class A1
{
virtual void a() = 0;
};
class A2
{
virtual int a(int x) = 0;
};
class B : public A1, public A2
{
void a() {}
int a(int x) { return x; }
};
int main()
{
A1* pa1;
pa1 = new B;
delete pa1;
A2* pa2;
pa2 = new B;
delete pa2;
return 0;
}
Classes A1 and A2 are just pure abstract, so multiple inheritance should do no harm. Now, the above code will cause a crash during destructor call, but what is peculiar, only for one object: pa2. The fix to this problem seems quite obvious - use virtual destructors ~A1() and ~A2(). However, there are still two questions:
Why the virtual destructors are necessary, since we do not have any data in any of these classes?
Why is the behavior different for pa1 and pa2? I have found that this is related to the order in which classes are placed on the parent list. If you changed it to:
class B : public A2, public A1
then
delete pa1;
would cause crash.

A possible and typical memory layout:
+-A1---+
| vptr |
+------+
+-A2---+
| vptr |
+------+
+-B------------------+
| +-A1---+ +-A2---+ |
| | vptr | | vptr | |
| +------+ +------+ |
+--------------------+
vptr is a pointer that points to some information about the most-derived type, e.g. the virtual function table, RTTI etc. (see e.g. the Itanium C++ ABI vtable layout)
So, when you write A2* p = new B, you'll end up with:
+-B------------------+
| +-A1---+ +-A2---+ |
| | vptr | | vptr | |
| +------+ +------+ |
+-----------^--------+
^ | p
| new B
When you now delete p;, this can cause trouble in the free store deallocator, since the address stored in p is not the same as the address you've received from the allocator (new B). This won't happen if you cast to A1, i.e. A1* p = new B, since there's no offset in this case.
Live example
You can avoid try to avoid this particular problem by restoring the original pointer via a dynamic_cast:
delete dynamic_cast<void*>(p);
Live example
But do not rely on this. It is still Undefined Behaviour (see Barry's answer).

From [expr.delete]:
In the first alternative (delete object), if the static type of the object to be deleted is different from its
dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the
static type shall have a virtual destructor or the behavior is undefined.
Undefined behavior is undefined. The virtual destructor is necessary because the standard says so (see also dyp's answer)
Compiling with warnings also helps:
main.cpp: In function 'int main()':
main.cpp:22:12: warning: deleting object of abstract class type 'A1' which has non-virtual destructor will cause undefined behaviour [-Wdelete-non-virtual-dtor]
delete pa1;
^
main.cpp:26:12: warning: deleting object of abstract class type 'A2' which has non-virtual destructor will cause undefined behaviour [-Wdelete-non-virtual-dtor]
delete pa2;
^

The order is sort of relevant because the order of destructors is opposite of the declaration order. However, it is actually "lucky" that it even works for pa1, since deleting objects of abstact class type with non-virtual destructor causes undefined behaviour. One always needs to add a virtual destructor for abstract classes.

Related

Pointer casting offset of a class with single inheritance

While learning the "Effective C++", I was firstly surprised when I learned the fact that if a class had multiple inheritance, its pointer may take offset when the pointer casting is done. Although it was not easy concept to grasp, but I think I managed to get it.
However, the author claims that this offset might happen even in the pointer casting of singly inherited class. I wonder what would be the such case, and wish to know the rationale behind it.
This can happen when polymorphism is introduced into a class hierarchy by a derived class. Consider the following class:
struct Foo
{
int a;
int b;
};
This class is not polymorphic, and thus the implementation does not need to include a pointer to a virtual dispatch table (a commonly-used method of implementing virtual dispatch). It will be laid out in memory like this:
Foo
+---+
a | |
+---+
b | |
+---+
Now consider a class that inherits from Foo:
struct Bar : Foo
{
virtual ~Bar() = default;
};
This class is polymorphic, and so objects of this class need to include a pointer to a vtable so further derived classes can override Bar's virtual member functions. That means that Bar objects will be laid out in memory like this:
Bar
+---------+
vtable pointer | |
+---------+
Foo subobject | +---+ |
| a | | |
| +---+ |
| b | | |
| +---+ |
+---------+
Since the object's Foo subobject is not at the beginning of the object, any Foo* initialized from a pointer to a Bar object will need to be adjusted by the size of a pointer so that it actually points at the Bar object's Foo subobject.
Live Demo
class B {
int a = 0;
};
class D : public B {
virtual ~D() = default;
};
D has a virtual member. B does not. A common implementation of dynamic dispatch in C++ involves keeping a hidden pointer to a table of function addresses at the begining of the object.
This means the first byte of the B sub-object won't be at the start of the complete D object. A pointer cast would need to adjust the address by the vptr size.

Why does virtual inheritance need a vtable even if no virtual functions are involved?

I read this question: C++ Virtual class inheritance object size issue, and was wondering why virtual inheritance results in an additional vtable pointer in the class.
I found an article here: https://en.wikipedia.org/wiki/Virtual_inheritance
which tells us:
However this offset can in the general case only be known at runtime,...
I don't get what is runtime-related here. The complete class inheritance hierarchy is already known at compile time. I understand virtual functions and the use of a base pointer, but there is no such thing with virtual inheritance.
Can someone explain why some compilers (Clang/GCC) implement virtual inheritance with a vtable and how this is used during runtime?
BTW, I also saw this question: vtable in case of virtual inheritance, but it only points to answers related to virtual functions, which is not my question.
The complete class inheritance hierarchy is already known in compile time.
True enough; so if the compiler knows the type of a most derived object, then it knows the offset of every subobject within that object. For such a purpose, a vtable is not needed.
For example, if B and C both virtually derive from A, and D derives from both B and C, then in the following code:
D d;
A* a = &d;
the conversion from D* to A* is, at most, adding a static offset to the address.
However, now consider this situation:
A* f(B* b) { return b; }
A* g(C* c) { return c; }
Here, f must be able to accept a pointer to any B object, including a B object that may be a subobject of a D object or of some other most derived class object. When compiling f, the compiler doesn't know the full set of derived classes of B.
If the B object is a most derived object, then the A subobject will be located at a certain offset. But what if the B object is part of a D object? The D object only contains one A object and it can't be located at its usual offsets from both the B and C subobjects. So the compiler has to pick a location for the A subobject of D, and then it has to provide a mechanism so that some code with a B* or C* can find out where the A subobject is. This depends solely on the inheritance hierarchy of the most derived type---so a vptr/vtable is an appropriate mechanism.
However this offset can in the general case only be known at runtime,...
I can't get the point, what is runtime related here. The complete class inheritance hierarchy is already known in compile time.
The linked article at Wikipedia provides a good explanation with examples, I think.
The example code from that article:
struct Animal {
virtual ~Animal() = default;
virtual void Eat() {}
};
// Two classes virtually inheriting Animal:
struct Mammal : virtual Animal {
virtual void Breathe() {}
};
struct WingedAnimal : virtual Animal {
virtual void Flap() {}
};
// A bat is still a winged mammal
struct Bat : Mammal, WingedAnimal {
};
When you careate an object of type Bat, there are various ways a compiler may choose the object layout.
Option 1
+--------------+
| Animal |
+--------------+
| vpointer |
| Mammal |
+--------------+
| vpointer |
| WingedAnimal |
+--------------+
| vpointer |
| Bat |
+--------------+
Option 2
+--------------+
| vpointer |
| Mammal |
+--------------+
| vpointer |
| WingedAnimal |
+--------------+
| vpointer |
| Bat |
+--------------+
| Animal |
+--------------+
The values contained in vpointer in Mammal and WingedAnimal define the offsets to the Animal sub-object. Those values cannot be known until run time because the constructor of Mammal cannot know whether the subject is Bat or some other object. If the sub-object is Monkey, it won't derive from WingedAnimal. It will be just
struct Monkey : Mammal {
};
in which case, the object layout could be:
+--------------+
| vpointer |
| Mammal |
+--------------+
| vpointer |
| Monkey |
+--------------+
| Animal |
+--------------+
As can be seen, the offset from the Mammal sub-object to the Animal sub-object is defined by the classes derived from Mammal. Hence, it can be defined only at runtime.
The complete class inheritance hierarchy is already known at compiler time. But all the vptr related operations, such as to get the offsets to virtual base class and issue the virtual function call, are delayed until runtime, because only at runtime can we know the actual type of the object.
For example,
class A() { virtual bool a() { return false; } };
class B() : public virtual A { int a() { return 0; } };
B* ptr = new B();
// assuming function a()'s index is 2 at virtual function table
// the call
ptr->a();
// will be transformed by the compiler to (*ptr->vptr[2])(ptr)
// so a right call to a() will be issued according to the type of the object ptr points to

Why does C-style cast behave differently than dynamic_cast?

I have the following class hierarchy:
class IControl
{
virtual void SomeMethod() = 0; // Just to make IControl polymorphic.
};
class ControlBase
{
public:
virtual int GetType() = 0;
};
class ControlImpl : public ControlBase, public IControl
{
public:
virtual void SomeMethod() { }
virtual int GetType()
{
return 1;
}
};
I have an IControl abstract class, and a ControlBase class. The ControlBase class does not inherit from IControl, but I know that every IControl-implementation will derive from ControlBase.
I have the following test code in which I cast an IControl-reference to ControlBase (because I know it derives from it) with dynamic_cast, and also with C-style cast:
int main()
{
ControlImpl stb;
IControl& control = stb;
ControlBase& testCB1 = dynamic_cast<ControlBase&>(control);
ControlBase& testCB2 = (ControlBase&)control;
ControlBase* testCB3 = (ControlBase*)&control;
std::cout << &testCB1 << std::endl;
std::cout << &testCB2 << std::endl;
std::cout << testCB3 << std::endl;
std::cout << std::endl;
std::cout << testCB1.GetType() << std::endl; // This properly prints "1".
std::cout << testCB2.GetType() << std::endl; // This prints some random number.
std::cout << testCB3->GetType() << std::endl; // This prints some random number.
}
Only the dynamic_cast works properly, the other two casts give back slightly different memory addresses, and the GetType() function gives back incorrect values.
What is the exact reason for this? Does the C-style cast end up using a reinterpret_cast? Is it related to how polymorphic objects are aligned in memory?
I think the class names in your example are a bit confusing. Let's call them Interface, Base and Impl. Note that Interface and Base are unrelated.
The C++ Standard defines the C-style cast, called "explicit type conversion (cast notation)" in [expr.cast]. You can (and maybe should) read that whole paragraph to know exactly how the C-style cast is defined. For the example in the OP, the following is sufficient:
A C-style can performs a conversion of one of [expr.cast]/4:
const_cast
static_cast
static_cast followed by const_cast
reinterpret_cast
reinterpret_cast followed by const_cast
The order of this list is important, because:
If a conversion can be interpreted in more than one of the ways listed above, the interpretation that appears first in the list is used, even if a cast resulting from that interpretation is ill-formed.
Let's examine your example
Impl impl;
Interface* pIntfc = &impl;
Base* pBase = (Base*)pIntfc;
A const_cast cannot be used, the next element in the list is a static_cast. But the classes Interface and Base are unrelated, therefore there is no static_cast that can convert from Interface* to Base*. Therefore, a reinterpret_cast is used.
Additional note: the actual answer to your question is: as there is no dynamic_cast in the list above, a C-style cast never behaves like a dynamic_cast.
How the actual address changes is not part of the definition of the C++ language, but we can make an example of how it could be implemented:
Each object of a class with at least one virtual function (inherited or own) contains (read: could contain, in this example) a pointer to a vtable. If it inherits virtual functions from multiple classes, it contains multiple pointers to vtables. Because of empty base class optimization (no data members), an instance of Impl could look like this:
+=Impl=======================================+
| |
| +-Base---------+ +-Interface---------+ |
| | vtable_Base* | | vtable_Interface* | |
| +--------------+ +-------------------+ |
| |
+============================================+
Now, the example:
Impl impl;
Impl* pImpl = &impl;
Interface* pIntfc = pImpl;
Base* pBase = pImpl;
+=Impl=======================================+
| |
| +-Base---------+ +-Interface---------+ |
| | vtable_Base* | | vtable_Interface* | |
| +--------------+ +-------------------+ |
| ^ ^ |
+==|==================|======================+
^ | |
| +-- pBase +-- pIntfc
|
+-- pimpl
If you instead do a reinterpret_cast, the result is implementation-defined, but it could result in something like this:
Impl impl;
Impl* pImpl = &impl;
Interface* pIntfc = pImpl;
Base* pBase = reinterpret_cast<Base*>(pIntfc);
+=Impl=======================================+
| |
| +-Base---------+ +-Interface---------+ |
| | vtable_Base* | | vtable_Interface* | |
| +--------------+ +-------------------+ |
| ^ |
+=====================|======================+
^ |
| +-- pIntfc
| |
+-- pimpl +-- pBase
I.e. the address is unchanged, pBase points to the Interface subobject of the Impl object.
Note that dereferencing the pointer pBase takes us to UB-land already, the Standard doesn't specify what should happen. In this exemplary implementation, if you call pBase->GetType(), the vtable_Interface* is used, which contains the SomeMethod entry, and that function is called. This function doesn't return anything, so in this example, nasal demons are summoned and take over the world. Or some value is taken from the stack as a return value.
What is the exact reason for this?
The exact reason is that dynamic_cast is guaranteed to work in this situation by the standard, while the other kinds invoke undefined behaviour.
Does the C-style cast end up using a reinterpret_cast?
Yes, in this case it does. (A side note: never ever use a C-style cast).
Is it related to how polymorphic objects are aligned in memory?
I would say it is related to the way polymorphic objects that use multiple inheritance are laid out in memory. In a language with single inheritance, dynamic_cast would not be necessary, as the base subobject address would coincide with the derived object address. In the multiple-inheritance case this is not so, as there are more than one base subobjects, and different base subobjects must have different addresses.
Sometimes the compiler can calculate the offset between each subobjects address and the derived object address. If the offset is non-zero, the cast operation is then becomes a pointer addition or subtraction instead of a no-op. (In the case of virtual inheritance upcast, it's somewhat more complicated but the compiler can still do that).
There is at least two cases when the compiler cannot do that:
Cross-cast (that is, between two classes neither of which is a base class of the other).
Downcast from a virtual base.
In these cases dynamic_cast is the only way to cast.

C++ "Virtual functions handling on multiple base classes"

I need to derive a child class CDerived from two different base classes CBaseA and CBaseB.
In addition, I need to call virtual functions of both parents on the derived class. Since I want to manage differently typed objects in one single vector later (this is not part of this minimal code expample), I need to call the virtual functions from a base class pointer to the derived class object:
#include <iostream>
#include <stdlib.h>
class CBaseA
{
public:
virtual void FuncA(){ std::cout << "CBaseA::FuncA()" << std::endl; };
};
class CBaseB
{
public:
virtual void FuncB(){ std::cout << "CBaseB::FuncB()" << std::endl; };
};
class CDerived : public CBaseB, public CBaseA
{};
int main( int argc, char* argv[] )
{
// An object of the derived type:
CDerived oDerived;
// A base class pointer to the object, as it could later
// be stored in a general vector:
CBaseA* pAHandle = reinterpret_cast<CBaseA*>( &oDerived );
// Calling method A:
pAHandle->FuncA();
return 0;
}
Problem: But when running this on my computer, FuncB() is called instead of FuncA(). I get the right result, if I "flip" the parent class deklarations around, i.e.
class CDerived : public CBaseA, public CBaseB
but this doesn't solve my problem, since I cannot be sure which function will be called.
So my question is: What am I doing wrong and what is the correct way of handling such a problem?
(I am using g++ 4.6.2, by the way)
CBaseA* pAHandle = reinterpret_cast<CBaseA*>( &oDerived );
Do not use reinterpret_cast for performing a conversion to a base class. No cast is required; the conversion is implicit:
CBaseA* pAHandle = &oDerived;
For converting to a derived class, use static_cast if the object is known to be of the target type or dynamic_cast if it is not.
Your use of reinterpret_cast yields undefined behavior, hence the "odd" behavior that you see. There are few correct uses of reinterpret_cast and none of them involve conversions within a class hierarchy.
Common implementation which may help you to understand what happens.
CBaseA in memory look like this
+---------+
| __vptrA |
+---------+
CBaseB in memory looks like this
+---------+
| __vptrB |
+---------+
CDerived looks like this:
+---------+
&oDerived-> | __vptrB |
| __vptrA |
+---------+
If you simply assign &oDerived to a CBaseA*, the compiler puts code to add the offset so that you have
+---------+
&oDerived--->| __vptrB |
pAHandle---->| __vptrA |
+---------+
an during execution the program find pointers to A virtual function in __vptrA. If you static_cast or dynamic_cast pAHandle back to a CDerived (or even dynamic_cast pAHandle to a CBaseA), the compiler will put code to subtract the offset so that the result point to the start of the object (dynamic_cast will find the information about how much to substract in the vtable along with the pointers to virtual functions).
When you reinterpret_casted &oDerived as a CBaseA*, the compiler don't put such code to adjust the pointer, you get
+---------+
pAHandle, &oDerived--->| __vptrB |
| __vptrA |
+---------+
and during the execution, the program looked at __vptrB for A virtual function, finding instead B virtual functions.

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