Umwandeln einer nicht polymorphen Basisklasse in konkrete Klasse



  • Hallo zusammen,

    ich lese gerade das Buch "Effektiv C++", darin wird geschrieben, dass eine Basisklasse ohne virtuelle Funktionen keinen virtuellen Dekunstruktor braucht => die Basisklasse ist nicht mehr polymorph. Dies hat für mich sogar einige Vorteile. Jedoch weiß ich leider nicht wie man mit diesen Klassen umgehen soll...

    class C
    {
    public:
           const long ValueC;
    
    protected:
           C(long c) :ValueC(c) {}
    };
    
    class A :public C
    {
    public:
           A(unsigned int a, long c) :C(c), ValueA(a) {}
           const unsigned int ValueA;
    };
    
    class B :public C
    {
    public:
           B(std::string b, long c) :C(c), ValueB(b) {}
           const std::string ValueB;
    };
    

    Doch leider ist nun folgendes möglich:

    std::vector<C*> items =
    {
    new A(1,-1),
    new B("B",-1)
    };
    auto a = static_cast<B*>(items.at(0));
    

    Mit dynamic_cast (nur möglich, wenn man eine virtuelle Methode in C hat) würde man einen nullptr bekommen. Wie kann man dies nun für nicht polymorphe Klassen implementieren. Am liebsten wäre es wie in C# mit dem "as".

    Schöne Grüße
    Quaneu



  • Erkläre den Sinn eines solchen Vectors.



  • Z.B. ein vector<Tiere>, Tiere ist meine Basisklasse. Katzen und Hunde sind meine konkreten Klassen.

    std::vector<Tiere*> items =
    {
    new Katze("A"),
    new Hund("B")
    };
    


  • Alle Tiere können fressen und scheissen.



  • Das war nur ein Beispiel. Meine Basisklasse hat keine Methoden, bzw. nur Felder. und die Ableitungen fügen nur Felder hinzu. Um genau zu sein, es sind DTOs.



  • Quaneu schrieb:

    Das war nur ein Beispiel.

    Es wäre eine Gelegenheit gewesen, über Sinn und Unsinn deines Vorhabens nachzudenken. Stattdessen kommt "nur ein Beispiel ".

    Meine Basisklasse hat keine Methoden, bzw. nur Felder. und die Ableitungen fügen nur Felder hinzu. Um genau zu sein, es sind DTOs.

    Und warum packt man die in einen Vector und vergisst, was sie beinhalten?



  • Entschuldige, ich dachte anhand eines Beispiel wird es klar. Aber ich kann es auch gern genauer beschreiben.

    Ich habe einen Parser geschrieben, der DWARF bzw. ELF Dateien analysiert. Hier gibt es laut Spec z.B. DebugInformationEntries, die DebugInformationEntryAttributes besitzen. Diese DebugInformationEntryAttributes können nun verschieden Ausprägungen besitzen z.B. Block1 Block2, Block4 usw..
    Daher besitzt meine Klasse DebugInformationEntry einen vector mit DebugInformationEntryAttributes. Nach dem Einlesen kann ich nun mit Hilfe der DebugInformationEntryAttribute Informationen ausgeben usw.



  • Quaneu schrieb:

    Mit dynamic_cast (nur möglich, wenn man eine virtuelle Methode in C hat) würde man einen nullptr bekommen. Wie kann man dies nun für nicht polymorphe Klassen implementieren.

    Schritt
    a) Du willst keine polymorphen Klassen haben
    b) Du willst aber polymorphes Verhalten haben
    c) Du stellst fest, dass du b) irgendwie ein Problem unter der Voraussetzung a) darstellt...

    Ich frage mich, ob du nicht lieber Punkt a) überdenken solltest... Was spricht jetzt genau dagegen, in class C einfach virtual ~C() = default; einzufügen?



  • Soweit ich es verstehe sind Klassen mit virtuellen Methoden 8 Byte (bzw. 4) größer als ohne. Da aber DebugInformationEntryAttribute die Klasse ist, die am häufigsten instanziiert wird, dachte ich mir, kann ich mir diese sparen.

    Bei meinem Beispiel schaffe ich es auch ohne polymorphe Basisklasse, da DebugInformationEntryAttribute ein Feld besitzt, das den Typen beinhalten. Somit kann ich in einem switch case static_cast benutzen.

    Ich wollte eigentlich nur wissen, ob es für a) auch andere Möglichkeiten gibt. Da dies aber ohne weiteres nicht immer möglich ist, muss ich bei diesen Fällen polymorphen Basisklassen benutzen.



  • Quaneu schrieb:

    darin wird geschrieben, dass eine Basisklasse ohne virtuelle Funktionen keinen virtuellen Dekunstruktor braucht => die Basisklasse ist nicht mehr polymorph.

    Ja und nein.

    Du brauchst eben eine virtuelle Funktion um von einem Basisklassenzeiger aus etwas zu tun. Dafür musst du objektorientiert denken.


  • Mod

    Quaneu schrieb:

    Bei meinem Beispiel schaffe ich es auch ohne polymorphe Basisklasse, da DebugInformationEntryAttribute ein Feld besitzt, das den Typen beinhalten. Somit kann ich in einem switch case static_cast benutzen.

    Nichts hindert dich daran, eine eigene cast-Funktion zu schreiben, etwa

    #include <type_traits>
    #include <typeinfo>
    #include <utility>
    
    /////////////
    template <typename T> struct tag_val { int val; };
    template <typename T, int I> struct derived_tag {};
    
    template <typename... T> struct tag_list;
    
    template <typename... T, int... I> struct tag_list<derived_tag<T, I>...> : tag_val<T>... {
        constexpr tag_list() : tag_val<T>{I}... {}
        template <typename U>
        constexpr int get() const {
            return tag_val<U>::val;
        }
    };
    
    struct base { int tag; };
    
    struct derived1 : base {};
    struct derived2 : base {};
    struct derived3 : base {};
    
    // Liste mit allen abgeleiteten Klassen mit zugehörigem tag-Wert
    constexpr tag_list<
        derived_tag<derived1, 1>,
        derived_tag<derived2, 2>,
        derived_tag<derived3, 3>> derived_tags;
    
    template <typename T, typename std::enable_if<std::is_pointer<T>{}>::type* = nullptr>
    T my_polymorphic_cast(base* p) {
        return p && derived_tags.get<typename std::remove_pointer<T>::type>() == p->tag ? static_cast<T>( p ) : nullptr;
    }
    
    template <typename T, typename std::enable_if<std::is_reference<T>{}>::type* = nullptr>
    T my_polymorphic_cast(base& p) {
        auto q = my_polymorphic_cast<typename std::remove_reference<T>::type*>( &p );
        return q ? q : throw std::bad_cast();
    }
    
    #include <iostream>
    
    int main() {
        derived1 a; a.tag = 1;
        base* p = &a;
        std::cout << (my_polymorphic_cast<derived1*>(p)!=nullptr) << '\n'
                  << (my_polymorphic_cast<derived2*>(p)==nullptr) << '\n';
    }
    

Anmelden zum Antworten