Could anyone help me understand this behaviour? To be short:
Here is what I have boilt it down to:
#include <iostream>
#include <vector>
// A base class
struct Base {
// A polymorphic method
virtual void describe() const {
std::cout << "Base" << std::endl;
};
virtual ~Base(){
std::cout << " Base destroyed" << std::endl;
};
};
// A specific interface
struct Interface {
virtual ~Interface(){
std::cout << " Interface Destroyed" << std::endl;
};
virtual void specific() = 0;
};
// A derived class..
struct Derived : public Base, public Interface {
virtual void describe() const {
std::cout << "Derived" << std::endl;
};
virtual void specific() {
std::cout << "Derived uses Interface" << std::endl;
};
virtual ~Derived() {
std::cout << " Derived destroyed" << std::endl;
};
};
int main() {
// Test polymorphism:
Base* b( new Base() );
Derived* d( new Derived() );
b->describe(); // "Base"
d->describe(); // "Derived"
// Ok.
// Test interface:
d->specific(); // "Derived uses Interface"
Interface* i(d);
i->specific(); // "Derived uses Interface"
// Ok.
// Here is the situation: I have a container filled with polymorphic `Base`s
std::vector<Base*> v {b, d};
// I know that this one implements the `Interface`
Interface* j((Interface*) v[1]);
j->specific(); // " Derived destroyed"
// " Interface destroyed"
// " Base destroyed"
// Why?! What did that object do to deserve this?
return EXIT_SUCCESS; // almost -_-
}
Can anyone tell me what I am missing there?
Interesting fact: If I swap the definitions of Base::~Base
and Base::describe
, then the object describes itself instead of being destroyed. How come the order matters in method declarations?
This is a good reason to avoid C-style casts. When you do:
Interface* j((Interface*) v[1]);
That is a reinterpret_cast
. A C-style cast will try to do, in order: const_cast
, static_cast
, static_cast
then const_cast
, reinterpret_cast
, reinterpret_cast
then const_cast
. All of these casts, in this case, are wrong! reinterpret_cast
in particular will just be undefined behavior and it honestly doesn't even matter why you see the specific behavior you see†. Undefined behavior is undefined.
What you want to do instead is:
Interface* j = dynamic_cast<Interface*>(v[1]);
That is the correct cast through the runtime dynamic hierarchy, and will give you the correct Interface*
corresponding to v[1]
(or nullptr
, if v[1]
did not have this runtime type). Once we fix that, then j->specific()
prints Derived uses Interface
, as you would expect.
Base
does not have a specific
, it's possible that the offset of that particular function lines up with ~Base()
, so the effect is that you're directly calling the destructor - which is why you see what you see.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With