Welcher Cast von void* auf T*



  • Abend,

    wenn ich von void* auf T* casten muss, welchen cast benutz ich dann? static_cast oder dynamic_cast?



  • static_cast .



  • dynamic_cast ist um einen zeiger auf eine basisklasse auf einen zeiger auf ein derivat zu casten.

    static_cast konvertiert zwischen den datentypen hin und her

    und reinterpret_cast interpretiert das an der übergebenen speicheraddresse vorhandene bitmuster als anderen datentypen



  • Bedenke, dass

    T* ----------> void* ----------> T*
            |                 |
       static_cast       static_cast
      oder implizit
    

    problemlos so funktioniert, wie man es gerne hätte (verlustfreie Rundreise) wohin gegen so etwas wie

    Derived* ----------> void* ----------> Base*               (Falsch!)
                  |                 |
             static_cast       static_cast
            oder implizit
    

    nicht dasselbe ist wie

    Derived* --> Base* ----------> void* ----------> Base*     (Richtig!)
              \______       |                 |
                     \ static_cast       static_cast
                      oder implizit
    

    Mit anderen Worten: Du darfst einen void* nur dorthin zurückkonvertieren, woher er genommen ist (wenn man const und volatile mal ignoriert).



  • Skym0sh0 schrieb:

    dynamic_cast ist um einen zeiger auf eine basisklasse auf einen zeiger auf ein derivat zu casten.

    Wie static_cast

    Skym0sh0 schrieb:

    static_cast konvertiert zwischen den datentypen hin und her

    Wie const_cast , dynamic_cast , reinterpret_cast

    ptr_conversion schrieb:

    [...]

    Gut erklärt! Als Ergänzung sei vielleicht noch gesagt, dass man void* relativ selten benötigt. Z.B. für Low-Level-Speicherverwaltung oder als eine Art Type Erasure, die zu weniger Template-Bloat führt (wie z.B. in Boost.PtrContainer).



  • Hallo,
    ich habe im Moment auch ein Cast-Problem. Ich schreib es mal hier rein, da es hier eh schon ums Casten geht:

    Folgendes habe ich (nur beispielhaft):

    struct base
    {
    	int a;
    }
    
    struct derived1 : public base
    {
    	std::vector<int> b;
    }
    
    struct derived2 : public base
    {
    	int c;
    }
    

    (in echt sind die Structs natürlich um einiges komplexer und es gibt mehr abgeleitete).

    Nun habe ich ein Methode die viele Dateien läd und je nach Informationen in die verschiedenen derived Structs schreibt. Folgendes kommt dann zurück:

    std::vector<std::pair<std::string, std::vector<base>>>
    //std::string ist der Typname, und im Vektor werden alle Struktur-Objekte des Typs gespeichert
    

    Nach dem mein Hauptprogramm nun die Daten geladen hat und in diesem Konstrukt hat, will es durch diese iterieren und nutzen.

    std::vector<std::pair<std::string, std::vector<base>>> resultVector = loadAll();
    for(std::vector<std::pair<std::string, std::vector<base>>>::iterator iter = resultVector.start; ...)
    {
    	for(std::vector<base> iter2 = iter->second.begin(); ...)
    	{
    		if(iter->first == "1")
    			//hier weiß ich, dass es derived1 sein muss
    		else if(iter->first == "2")
    			//hier weiß ich, dass es derived2 sein muss
    	}
    }
    

    Das ist wieder so weit runter gebrochen, dass das wichtigste da ist. z.b. wird normalerweise nicht direkt vorm durch iterieren erst geladen.

    So aber jetzt muss ich base nach derived1 casten können und genau das schaffe ich nicht. hab es schon mit dynamic_cast, static_cast und explizite Conversion versucht aber alles klappt nicht.
    dynamic_cast will nur derived zu base machen aber nicht umgekehrt.

    z.B. der static_cast:

    derived1* strategy = static_cast<derived1*>(&(*iter2));
    

    Kompiliert aber nach dem Casten steht Müll drin.

    Wie mache ich es richtig?

    Danke für eure Hilfe



  • Hier findet Slicing statt, da du die Objekte per Value in den vector speicherst. Du musst Zeiger verwenden, dafür eignet sich z.B. boost::ptr_vector oder ein std::vector<unique_ptr<T>>.



  • danke
    so hats funktioniert. wollte um das "new" rum kommen.

    Habe nun einen

    std::vector<std::pair<std::string, std::vector<base*>>>
    

    was haben boost::ptr_vector und std::vector<unique_ptr<T>> für Vorteile?



  • Auch wenn ich etwas zu langsam war:
    Kuck dir mal folgendes an:

    #include <vector>
    
    class FooBar{
    public:
            virtual ~FooBar(){} // Deine Basisklasse braucht eine virtuelle Funktion oder Destruktor für einen dynamic_cast
    };
    class Foo : public FooBar{
            void foo(){}
    }
    class Bar : public FooBar{
            void bar(){}
    };
    
    int main(){
            std::vector<FooBar*> vec; // Du brauchst einen Vektor von Pointern, da sonst der Copy-Konstruktor der Basis-Klasse aufgerufen wird - Alle Informationen der abgeleiteten Klassen incl. v-table gehen verloren
            vec.push_back(new Foo());
            vec.push_back(new Bar());
            for(std::vector<FooBar*>::const_iterator it = vec.begin(); it!=vec.end(); ++it){
                    if(Foo* foo = dynamic_cast<Foo*>(*it)){ // so kannst du testen, ob ein Zeiger auf ein Objekt von einem bestimmten Typ ist - dynamic_cast liefert 0, wenn es nicht der Fall ist
                            foo->foo();
                    }
                    if(Bar* bar = dynamic_cast<Bar*>(*it)){
                            bar->bar();
                    }
            }
            // Löschen der Objekte nicht vergessen, weswegen sich evtl. ein boost::ptr_vector anbietet, der alle Objekte darin löscht, wenn er zerstört wird (siehe PI)
    }
    

    EDIT: Wenn dynamic_cast ohne Zeiger verwendet wird, fliegt, falls es nicht möglich ist, eine bad_cast Exception.

    Gruß,
    XSpille



  • Therese schrieb:

    was haben boost::ptr_vector und std::vector<unique_ptr<T>> für Vorteile?

    Du brauchst dich nicht um das delete kümmern, das wird dann für dich automatisch erledigt.



  • 314159265358979 schrieb:

    Therese schrieb:

    was haben boost::ptr_vector und std::vector<unique_ptr<T>> für Vorteile?

    Du brauchst dich nicht um das delete kümmern, das wird dann für dich automatisch erledigt.

    Falls du boost verwenden darfst, würde ich boost::ptr_vector verwenden, da std::vector<unique_ptr<T>> afaik keine Vorteile hat (außer dass man 0 drin speichern kann).



  • Im Normalfall besser als std::vector<std::shared_ptr<T>> 😉
    Aber ja, boost::ptr_vector ist zu bevorzugen.



  • 314159265358979 schrieb:

    Im Normalfall besser als std::vector<std::shared_ptr<T>> 😉
    Aber ja, boost::ptr_vector ist zu bevorzugen.

    Bring ihn nicht auf blöde Gedanken mit std::vector<std::shared_ptr<T>>, 😉
    sonst ist hier bald wieder jemand neues im Forum, der boost::shared_ptr<T> bzw. std::shared_ptr<T> als universellen 'Problemlöser' nutzt und Overhead in Kauf nimmt, den er in den seltensten Fällen nicht benötigt 👎


Anmelden zum Antworten