2015-08-18 6 views
12

Kann mir jemand helfen, dieses Verhalten zu verstehen? Um kurz:Seltsames Verhalten beim Extrahieren einer bekannten Schnittstelle aus einem polymorphen Container

  • ich polymorphe Objekte in einem gemeinsamen Behälter gespeichert haben.
  • Einige von ihnen implementieren eine bestimmte Schnittstelle. Ich kann sagen, welche.
  • Aber ich kann diese Schnittstelle nicht verwenden.

Hier ist, was ich boilt es haben bis zu:

#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 -_- 
} 

Kann mir jemand sagen, was ich dort bin fehlt?

Interessant: Wenn ich die Definitionen von Base::~Base und Base::describe tauschen, dann das Objekt beschreibt sich stattdessen zerstört zu werden. Wie kommt es, dass der Auftrag in Methodendeklarationen eine Rolle spielt?

Antwort

21

Dies ist ein guter Grund, C-Style-Casts zu vermeiden. Wenn Sie das tun:

Interface* j((Interface*) v[1]); 

Das ist ein reinterpret_cast. Ein C-Style-Cast wird versuchen, in der folgenden Reihenfolge zu tun: const_cast, static_cast, static_cast dann const_cast, reinterpret_cast, reinterpret_cast dann const_cast. Alle diese Abgüsse sind in diesem Fall falsch! reinterpret_cast insbesondere wird nur undefined Verhalten sein und es ist ehrlich gesagt nicht einmal egal, warum Sie das spezifische Verhalten sehen, das Sie sehen & dagger;. Nicht definiertes Verhalten ist nicht definiert.

Was wollen Sie stattdessen zu tun ist:

Interface* j = dynamic_cast<Interface*>(v[1]); 

, dass die richtige Besetzung durch die Laufzeit dynamische Hierarchie ist, und gibt Ihnen die richtigeInterface*-v[1] entsprechenden (oder nullptr, wenn v[1] nicht tat Habe diesen Laufzeittyp). Sobald wir das beheben, j->specific() druckt Derived uses Interface, wie Sie erwarten würden.


& dolch; Wahrscheinlich hat das Problem mit der Vtable-Ausrichtung zu tun. Wenn Sie die Umformung durchführen, da Base keine specific hat, ist es möglich, dass der Versatz dieser bestimmten Funktion mit ~Base() ausgerichtet ist, so dass Sie den Destruktor direkt aufrufen - weshalb Sie sehen, was Sie sehen .

+0

Aus Interesse, 'Interface * j = static_cast (v [1]);' scheint auch zu funktionieren, ist es sicher? –

+0

@ChrisDrew Solange Sie * wissen *, dass '* v [1]' tatsächlich eine Instanz von 'Derived' ist (oder eine davon abgeleitete Klasse), ist es sicher. – Angew

+1

@ChrisDrew Ja, das ist in Ordnung, weil du weißt, dass es ein 'Derived *' ist, also ist der 'static_cast' OK. Und dann Zeiger zu abgeleitet ('Derived *') zu Zeiger-zu-Basis ('Interface *') ist eine Standardkonvertierung, dass man immer OK ist. 'dynamic_cast' ist sicherer (aber langsamer), denn wenn' v [1] 'kein' Derived * 'wäre, würden Sie ein wohldefiniertes Ergebnis (einen Nullzeiger) anstelle eines gültig aussehenden Zeigers erhalten, der undefiniertes Verhalten ergibt wenn du es benutzt. – Barry