简体   繁体   中英

Weird behaviour when extracting a known interface from a polymorphic container

Could anyone help me understand this behaviour? To be short:

  • I have stored polymorphic objects in a common container.
  • Some of them implement a specific interface. I can tell which ones.
  • But I can't use this interface.

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.


Likely, the issue has to do with the vtable alignment. When you do the reinterpret cast, since 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM