Reputation: 15
I was making some excercise for learning inheritance and virtual methods in c++. So i found myself into this:
class A{
public:
virtual void f() const {cout << "A::f";}
virtual void g() {cout << "A::g"; m();}
virtual void h() {cout << "A::h"; f();}
void m() {cout << "A::m"; f();}
virtual A* n() { cout << "A::n"; return this;}
}
class B : public A{
public:
virtual void f() const {cout << "B::f";} //override
void g() {cout << "B::g"; A::n();} //override
virtual void m() {cout << "B::m"; f();}
A* n() { cout << "B::n"; return this;}
}
class C : public A{
public:
virtual void f() {cout << "C::f";} //new method cause misses const
void g() const {cout << "C::g"; m();} //new method cause added const
void m() {cout << "C::m"; g(); f();}
}
***
A*q3 = new C();
(static_cast<B*>(q3->n()))->f(); //solution is A::n A::f
***
So is this an error from the book or is it right? For me, from my knowledge, i would mark this as an "Undefined Behaviour", but if i am wrong, how does this work?
Upvotes: 1
Views: 159
Reputation: 515
This casts a C*
object to an A*
object (via n()
) and then casts an A*
object to B*
object. Finally it calls a virtual function.
So what happens in practice? The compiler will look to the virtual table of the underlying object and find there what function f
to call. The virtual table is an actual table in memory. So all the casting from A to B to C has no influence on the data in the table.
There is no undefined behaviour, and indeed A::f
will be called.
However, this works only for virtual functions. If the code were to call a member function of B using a C object, and if that function were to make use of member data available in B but not in C, all hell will break loose. Welcome to C++, where the ability to shoot yourself in the foot is a desired feature.
Upvotes: 0
Reputation: 133
static_cast can perform conversions between pointers to related classes, not only from the derived class to its base, but also from a base class to its derived. This ensures that at least the classes are compatible if the proper object is converted, but no safety check is performed during runtime to check if the object being converted is in fact a full object of the destination type. Therefore, it is up to the programmer to ensure that the conversion is safe. On the other side, the overhead of the type-safety checks of dynamic_cast is avoided.
You could easily cast to an object with more fields than the original, though the real object you point to is shorter. Then when you try to access those fields, it's like a buffer overflow - can yield very surprising results. Be very careful when doing this.
Upvotes: 1