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.
Related
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.
I have a class structure with loads of different child classes all inheriting from the same abstract base class. This base class is there because all these classes self register at compile time to a factory from which they are built. This is really neat and keeps me from the burden of maintaining a huge switch or any other flow mechanism somewhere down stream.
+---------------+
| Base |
+--+---------+--+
| |
+-------+-+ +-+-------+
| Kid1 | | Kid2 |
+----+----+ +----+----+
| |
+---+---+---+ +---+---+---+
| | | | ... | | | | ....
However, these Kid1 and Kid2 classes are different and the issue is that I have to distinguish between them somewhere in my code and the only thing I have is a Base pointer. I did not want to bother the factory with this and keep that as simple as possible.
The way I solved this right now is by having Base being somewhat aware of the two siblings. It has a virtual method type() which returns an enum type distinguishing between Kid1 and Kid2. Both kids override this (basically saying I am KidX) such that I know with whom I am dealing. This type is then used to dynamic_cast Base to either of the kids and the program proceeds.
However, is this the right approach?
I could for example add some more virtual methods to base, on the one hand polluting it with methods only one part of the hierarchy uses but on the other hand saving me the dynamic_cast.
Any thoughts?
In multiple inheritance, dynamic_cast will shift the pointer as necessary and is a must. Therefore, getting rid of it is dangerous. If your system extends to a point where it uses MI somewhere down, you might get all sorts of strange behaviour.
You don't need dynamic_cast, dynamic_cast already use internal runtime type information to determine if it's able to cast the passed pointer/reference to the desired type but you are already checking it through the type() method. A static_cast is enough in your situation, as you are already providing RTTI by yourself.
Or alternatively you could just remove the type() method and use directly dynamic_cast, which yields nullptr if it's not able to cast the object to your desired type.
If you really want to avoid such things then you are forced to encapsulate the behavior desired into virtual methods such that wherever you need to use a Kid1 or Kid2, you will have different implementation without the need to really distinguishing between the two, just by letting polymorphism do its work.
You could try the Visitor pattern.
When should I use the Visitor Design Pattern?
I generally prefer to be able to get a handle to the desired type without using a cast. This means adding methods to the Base class:
virtual Kid1 *asKid1()
{
return null;
}
virtual Kid2 *asKid2()
{
return null;
}
The subclasses then override these as appropriate:
// (in Kid1)
virtual Kid1 *asKid1() override
{
return this;
}
Although your Base class is still being "polluted" with extra methods, only these two methods are required, even if the subclasses are expanded in the future.
(You can have them return references rather than pointers, in which case they should throw an exception by default).
Maybe you could use typeid operator. Because:
N3337 5.2.8/2 says:
When typeid is applied to a glvalue expression whose type is a polymorphic class type (10.3), the result refers
to a std::type_info object representing the type of the most derived object (1.8) (that is, the dynamic
type) to which the glvalue refers ....
Example:
struct Base
{
virtual void f(){} //to ensure that Base is polymorphic object
};
struct Derived: Base{};
struct AnotherDerived: Base{};
int main()
{
Base *ptr = new AnotherDerived;
if(typeid(*ptr) == typeid(Derived)) //Do not forget to dereference pointer, otherwise typeid() will return type_info for pointer itself
{
cout << "ptr is pointing to object of type Derived" << endl;
}
if (typeid(*ptr) == typeid(AnotherDerived))
{
cout << "ptr is pointing to object of type AnotherDerived" << endl;
}
}
However if you have to distinguish between them in your code why are you using pointer to Base? What about separating code where you are using only Base properties and code where are you using specific properties of your child classes? That's the whole point of polymorphism.
EDIT:
As davmac pointed out. It will work only if these subclasses will be most derived classes.
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.
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.
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).