dynamic_cast & virtual



  • Du solltest dir nicht überlegen wie du dynamic_cast am besten nutzen kannst, sondern wie dein Programm am besten ohne dynamic_cast auskommt. Casts sind oft ein Ausdruck schlechten Stils und dynamic_cast schiesst dabei den Vogel vollkommen ab. Denn auf den meisten Systemen hat der einen Laufzeitoverhead, der nicht mal von Compiler zu Compiler konstant sein muss. Oft ist man da mit einem überarbeiteten Design besser bedient. Und wenn du schon keine virtuellen Funktionen hast, versteh ich auch nicht wozu der dynamic_cast gut sein soll. Oder geht dir's nur darum, mit dynamic_cast ein bissl rumzuspielen?



  • @RHBaum ups ja der dtor... stimmt mein fehler

    @groovemaster2002 der einzige grund für mich den dynamic_cast zu verwenden is sicherzustellen dass nich irgendwo schrottige pointer rumschwirren
    also das folgende assert ( pointer != NULL );

    gecastet wird bei mir eh nich in zeitkritischen passagen... somit is die performance wurscht

    in dem speziellen fall hatte die basisklasse nur protectete daten und funktionen die von den abgeleiteten benutzt wurden



  • Sovok schrieb:

    der einzige grund für mich den dynamic_cast zu verwenden is sicherzustellen dass nich irgendwo schrottige pointer rumschwirren
    also das folgende assert ( pointer != NULL )

    Versteh ich dich richtig, dass du mit Kanonen auf Spatzen schiesst? Wenn ja versteh ich das nicht. Was sind denn bei dir "schrottige pointer"?



  • The value of a failed cast to pointer type is the null pointer. (msdn)

    was auch immer failed cast heisst

    oder um more effective c++ zu zitieren

    "this is u use dynamic_cast to cast pointers or references to base class objects into pointers or references to derived or silbing class objects in such a way that you can determine whether the cast succeeded"

    im allgemeinen wiegt sicherheit für mich schwerer als schnelligkeit



  • mir is grad noch ein beispiel in meinem code aufgefallen in dem dynamic_cast sehr sinnvoll is

    QListViewItem *pQItem = m_lScreenList->selectedItem();
    
    if ( pQItem == NULL )
    {
      return;
    }
    

    hier hol ich aus nem qlistview ein qlistviewitem
    wenn kein item ausgewählt wurde kommt NULL zurück

    VListViewItem *pItem = dynamic_cast< VListViewItem* > ( pQItem );
    
    assert ( pItem != NULL );
    
    if ( pItem->GotDataPtr() == FALSE )
    {
      return;
    }
    

    jetzt caste ich das ganze in eine eigene klasse die ich von qlistviewitem abgeleitet hab um listenitems mit einem datenpointer zu verknüpfen

    es wäre durchaus möglich, dass ich irgendwo versehentlich ein qlistviewitem zur liste hinzugefügt habe, das kein vlistviewitem is



  • groovemaster2002 schrieb:

    Du solltest dir nicht überlegen wie du dynamic_cast am besten nutzen kannst, sondern wie dein Programm am besten ohne dynamic_cast auskommt. Casts sind oft ein Ausdruck schlechten Stils und dynamic_cast schiesst dabei den Vogel vollkommen ab.

    sorry, aber casts sind kein ausdruck schlechten stils, casts sollten zwar wenn möglich nur dann eingesetzt werden, wenn sie nötig sind, aber es steht auch nirgendwo, dass man nie casts benutzen sollte, dafür gibt es einfach zuviele fälle,in denen man sich ohne casts enorm beschneiden müsste:
    erklär mir mal, wie man ohne casts verschiedenste datentypen über eine schnittstelle laufen lassen kann,wenn man zur compilezeit nicht weis, welche typen man zu erwarten hat(plugin basiertes system), weshalb templates nicht möglich sind.
    Ohne void* casts wäre ich zumindest absolut aufgeschmissen^^

    was man aber umgehen sollte sind implizite casts in klassen(siehe modernes c++ design, das kapitel über smart pointer). implizite casts können hard to find bugs ergeben,explicite casts selten.

    Vorallem die angst vor dynamic_cast ist absolut unangebracht,dynamic_cast ist für factory classes von essentieller bedeutung, wenn es darum geht vielseitigen code zu schreiben.

    ein beispiel ist zb eine factory class in einer gui mit folgenden eigenschaften:
    -erstellung vordefinierter buttons
    -erstellung von zur laufzeit neu definierten buttons(zb durch plugins)
    -kommunikation mit den buttons nur über interfaces

    der letzte punkt ergibt sich eigentlich aus 1 und 2,da man die unterschiedlichen klassen zur laufzeit nur über interfaces unter einen hut bekommt,ein dynamic cast ist bei den eigenschaften nicht zu umgehen.



  • @otze
    Du hast wieder mal (wie so oft :p ) nicht verstanden, worum es ging. Ich hab nie gesagt, dass du keine Casts benutzen sollst. Ich hab lediglich gesagt, dass exzessives Casten meistens ein Ausdruck schlechten Stils ist. Wahrscheinlich hast du noch nicht so viel Fremdcode gesehen, aber leider gibt es zu viele Projekte, wo man durch ein besseres Design sich 'ne Menge Casts (und Probleme) hätte sparen können.
    Und das man implizite Casts eher vermeiden sollte als explizite Casts, halt ich für ein Gerücht. Im allgemeinen sind implizite Casts als ungefährlicher einzustufen, weshalb ein Compiler sie auch ohne Probleme durchführen kann. Was aber nicht heissen soll, dass es dadurch keine Probleme geben kann.



  • was man aber umgehen sollte sind implizite casts in klassen

    Das ist leicht, denn es gibt schlicht und einfach keine impliziten Casts. Implizite Casts sind im selben Land zu Hause wie der blondgelockte Jüngling mit kohlrabenschwarzem Haar.



  • otze schrieb:

    Ohne void* casts wäre ich zumindest absolut aufgeschmissen^^

    wird sich ändern, wenn du c++ lernst.

    was man aber umgehen sollte sind implizite casts in klassen(siehe modernes c++ design, das kapitel über smart pointer). implizite casts können hard to find bugs ergeben,explicite casts selten.

    explizite casts verbergen warnungen. wenn sich mein doofer int-typ andert, und vorher ging int i=foo() ohne warnung und jetzt kommt ne warnung, dann baue ich halt um.
    aber bei deinem int i=int(foo()) fliegt nie im leben mehr ne warnung. ende gelände. sense. code kaputt. daher gilt für mich: meide casts. wen es ohne warnng auch so geht, dann isses so auch besser.
    das bekannte srand(static_cast<unsigned int>(time(NULL)) ist inb dieser hinsicht einfach nur schlecht.
    natürlich muß man bei casts immer aufpassen, manchmal sind implizite casts schrecklich. dynamic_cast ist fast immer schrecklich. void* ist so schlimm wie goto. dynamic_cast ist nur so schlimm wie endlosschleifen.
    aber normalerweise unterhalte ich mich über viel unschrecklichere dinge. vermeidbare casts sind eigentlich außerhalb jeder diskussion.

    edit: oh, ich rede über was, was es gar nicht gibt. wie heißen implizite casts korrekt?



  • volkard schrieb:

    edit: oh, ich rede über was, was es gar nicht gibt. wie heißen implizite casts korrekt?

    Ein Cast ist eine *explizite* Typumwandlung. Die macht man entweder mit einer "Cast-Expression", oder der "functional notation" oder mit einem
    der vier sogenannte Cast-Operatoren: dynamic_cast, static_cast, reinterpret_cast und const_cast.

    Dann gibt es noch sogenannte *implizite* Typumwandlungen oder auch Typkonverierungen. Dazu gehören z.B. so Dinge wie Standardkonvertierungen, Integrale Promotionen usw, aber auch die sogenannten "conversion operators" (Konvertierungsoperatoren). Das sind die Dinger die man mit der Syntax operator Type() in Klassen definieren kann und die neben Konstruktoren die mit einem Argument aufrufbar sind für *implizite* Typumwandlungen von Objekten verwendet werden.

    Casts muss der Mensch selbst hinschreiben. Implizite Typumwandlungen (Konvertierungen) macht der Compiler von alleine. Definitiv ein Unterschied.



  • HumeSikkins schrieb:

    Casts muss der Mensch selbst hinschreiben. Implizite Typumwandlungen (Konvertierungen) macht der Compiler von alleine. Definitiv ein Unterschied.

    ok, da habe ich ein wenig geschludert und "casten" viel zu häufig für "typumwandeln" verwendet.

    gemein wirds bei umwandelnden konstruktionen wie

    for(int i=0;i<256;++i){
       char c=i;
       cout<<c;
    }
    

    da würde ich sagen: durch den trick mit dem char habe ich i vor dem ausgeben in einen char gecastet. dem wesen (schönes wort) nach wahr, aber formal völliger quatsch. und irgendwann fängt man an, auch nen cast zu denken, wenn man bewußt eine implizite typumwandlung heraufbeschwört.

    muß wohl so ein ding sein, wie die if-schleife, die es ja auch nicht gibt, aber die ich immer wieder lese.



  • volkard schrieb:

    gemein wirds bei umwandelnden konstruktionen wie

    for(int i=0;i<256;++i){
       char c=i;
       cout<<c;
    }
    

    Ich sehe hier nichts gemeines, ich sehe hier nur eine Integer-Konvertierung ;)Und die ist in C++ bekanntlich implizit. Das du die Zeile die zu der Konvertierung führt explizit hingeschrieben hast, macht die Konvertierung nicht explizit und insbesondere ist hier auch kein cast anwesend (was man leicht daran erkennt, dass hier weder irgendwelche Klammern unmotiviert rumstehen und auch von diesen ewig langen neuen C++ Schlüsselwörter mit Unterstrich und eckigen Klammern dran ist weit und breit nichts zu sehen).

    Manchmal ist eine Definition einfach eine Definition.

    muß wohl so ein ding sein, wie die if-schleife, die es ja auch nicht gibt, aber die ich immer wieder lese.

    Imo sind Begriffsdefinitionen in einem technischen Umfeld wie einer Programmiersprache sehr wichtig. Es gibt da aber durchaus verschiedene Level.
    Wenn jemand in dem oben zitierten Kontext davon spricht, dass er i vor der Ausgabe nach "char castet", dann ist das imo noch durchaus ok (es sei denn es geht gerade um das Thema Cast vs. implizite Typumwandlung).
    Wenn aber jemand von einer if-Schleife spricht, dass geht gar nicht.
    Ich für meinen Teil schalte in dem Moment bereits ab.



  • najut, dann halt implizite typumwandlungen, mir wurden sie zusammen mit den andren casts unter dem großen oberbegriff "casts" als "implizit" beigebracht...

    @volkard jo ich kann kein c++,wird sich sicher ändern, aber atm keine ahnung wie ich einen datentyp mit einer größe von 1-100byte zur laufzeit in einen parameter quetschen kann 🙄.
    vielelicht sollte ich ja doch statt nem void* ein char* nehmen 🙄
    vielleicht sollte ich mich auch damit abfinden,dass ich 2 plugins unter keinen umständen erlauben darf mittels eigener datentypen über die standardschnittstelle für plugins in meinem projekt miteinander zu kommunizieren, ja das wär sicher die beste möglichkeit 🙄.

    casts sind übrigens für mich warnung genug, immerhin sind sie a) deutlich hervorgehoben und b) von mir so gewollt, ich sehe genau was ich mit meinem typ mache, deshalb gefallen mir implizite typumwandlungen nicht, weil man es dem code im zweifesfall nicht ansieht.

    dynamic_cast ist fast immer schrecklich

    solange es aber noch mindestens eine sinnvolle möglichkeit gibt sie anzuwenden(und zwar immer dann wenn man von einer baseclass einen downcast machen muss...) haben sie eine daseinsberechtigung.



  • otze schrieb:

    solange es aber noch mindestens eine sinnvolle möglichkeit gibt sie anzuwenden(und zwar immer dann wenn man von einer baseclass einen downcast machen muss...) haben sie eine daseinsberechtigung.

    Wirklich? Für simple Downcasts kann ich auch static_cast nehmen. Erst wenn es zu etwas komplizierteren Hierarchien kommt und Crosscasts benötigt werden, könnte bzw. müsste man dynamic_cast in Erwägung ziehen...



  • otze schrieb:

    @volkard jo ich kann kein c++,wird sich sicher ändern, aber atm keine ahnung wie ich einen datentyp mit einer größe von 1-100byte zur laufzeit in einen parameter quetschen kann 🙄.

    sowas wie string oder vector?

    vielelicht sollte ich ja doch statt nem void* ein char* nehmen 🙄

    eher nicht.
    void* ist nur "schlimm" aber nicht per dogma verboten. wenn man nach eingehender überlegung keinen guten ausweg findet, nimmt man eben mal void*. wenn man keine characters hat, sondern zum beispiel ne struct, dann ist ein void* angemessener als ein char*.

    machmal verwendet man aus performancegründen void* und baut ne kostenlose! typsichere schnittstelle drumherum.

    //ungetestet
    class TinyVoidPtrStackAsSingleLinedList{
       struct Node{
          void* data;
          Node* next;
          Node(void* _data,Node* _next):
             data(_data),next(_next){
          }
       };
       Node* anchor;
       TinyVoidPtrStackAsSingleLinedList(TinyVoidPtrStackAsSingleLinedList const&);//forbidden
       TinyVoidPtrStackAsSingleLinedList& operator=(TinyVoidPtrStackAsSingleLinedList const&);//forbidden
    public:
       TinyVoidPtrStackAsSingleLinedList(){
          anchor=0;
       }
       ~TinyVoidPtrStackAsSingleLinedList(){
          while(!isEmpty())
             pop();
       }
       bool isEmpty(){
          return anchor==0;
       }
       void* peek(){
          return anchor->data;
       }
       void pop(){
          Node* toDie=anchor;
          anchor=anchor->next;
          delete toDie;
       }
       void push(void* data){
          anchor=new Node(data,anchor);//SCNR
       }
    };
    
    //und ab hier keine kosten mehr. damit meine ich nicht 1000 takte oder 100 takte. 
    //damit meine ich 0 takte und 0 bytes. 
    template<class T>
    class TinyPtrStackAsSingleLinedList{
       TinyVoidPtrStackAsSingleLinedList m_tvpsasllData;
       bool isEmpty(){
          retrn m_tvpsasllData.isEmpty();
       }
       T* peek(){
          return static_cast<T*>(m_tvpsasllData.peek());
       }
       void pop(){
          return m_tvpsasllData.pop();
       }
       void push(T* data){
          return m_tvpsasllData.push(data)
       }
    };
    

    vielleicht sollte ich mich auch damit abfinden,dass ich 2 plugins unter keinen umständen erlauben darf mittels eigener datentypen über die standardschnittstelle für plugins in meinem projekt miteinander zu kommunizieren, ja das wär sicher die beste möglichkeit 🙄.

    wenn das geht, ohne sich ein bein zu brechen...
    evtl statt

    void send(char* data,int size);
    

    an sowas denken:

    void send(Packet* data);
    nebst
    struct /*class*/ Packet{
       void* data;
       int size;
    };
    

    das wiederum mutiert fast augenblicklich zu

    void send(Packet* data);
    nebst
    struct /*class*/ Packet{
       virtual ~Packkt(){
       }
    };
    

    je nach gusto sogar

    void send(auto_ptr<Packet> data);
    nebst
    struct /*class*/ Packet{
       virtual ~Packkt(){
       }
    };
    

    hier würde der empfänger sogar in der debug-version zur eigenen sicherheit einen dynamic_cast verwenden, aber nur, weil folgende lustige situation gegeben ist:
    - der sender weiß, daß er nur einen FooPacket* verschicken will
    - der empfänger weiß, daß er nur einen FooPacket* empfangen will
    - es wird ein Packet* auf die reise geschickt, weil die schnittstelle nix von FooPacket wußte.

    warum steht nicht gleich da

    void send(FooPacket* data);
    

    ?
    die beiden plugins, die mit FooPackets arbeiten, müßten halt gemeinsam die datei FooPacket.h kennen. und dann braucht man gar kein casten, keine basisklasse und vor allem kann an nicht aus versehen nen falschen typ versenden.
    jo, ich vermute, es geht, ohne sich ein bein zu brechen.

    dynamic_cast ist fast immer schrecklich

    solange es aber noch mindestens eine sinnvolle möglichkeit gibt sie anzuwenden(und zwar immer dann wenn man von einer baseclass einen downcast machen muss...) haben sie eine daseinsberechtigung.

    das haben wir früher auch anders lösen können.

    class Base{
       virtual Foo* toFooPtr(){return 0;}
       virtual Bar* toBarPtr(){return 0;}
       ...
    };
    class Foo{
       virtual Foo* toFooPtr(){return this;}
       ...
    };
    ...
    

    das war eigentlich auch ganz sicher, ohne weiteren rtti-overhead und sogar recht bequem. natürlich gibt es gründe, dynamic_cast zu haben, aber sichere downcasts aleine sind es nicht, die sind zu leicht so zu bekommen, daß man dafür ein schlüsselwort einführen würde.



  • @volkard soweit bin ich eigentlich auch schon gekommen,char* stand eigentlich auch nie zur debatte(genauso wie string oder vector),da ich einfach zuviele verschiedene datentypen erwarten muss.

    das polymorphe interface hatte ich zuerst auch geplant, aber dann wieder verworfen, da es nicht ohne exzessive benutzung von dynamic_cast ausgekommen wär(auch wenn ich nichts gegen dynamic_cast hab, das war einfach zuviel^^).

    dein basetrick funktioniert da übrigens auch nicht,mehr dazu aber später^^.

    wieso kommt das nicht ohne dynamic_cast aus?
    1. will ich an die daten kommen, muss ich sie abfragen,das interface kann mir diese funktionalität aber nicht bieten, da es zur compilezeit den typ nicht kennt. daraus folgt->dynamic_cast, um an das standardinterface zu kommen.
    zum glück kann ich in meinem fall von fast sicheren downcasts ausgehen(aber nur fast,ich sollte doch noch prüfen ob der datentyp wirklich der ist für den ich ihn halte), da mein send so aussieht:

    void send(Message message,Packet* packet
    

    das plugin kann also den datentyp in packet anhand der message indetifizieren.
    dh wenn zb in message "CreateShadowVolume" steht, kann ich davon ausgehen, dass in packet ein mesh und eine lichtquelle ist.
    Dasselbe könnte ich aber dann auch mit void* pointern erreichen, vielleicht sogar schneller.

    nun könnte man ja wie du gesagt hast mit

    void send(FooPacket* data);
    

    kommen, sicher das geht auch solange man nur einen datentyp zu erwarten hat, es ist aber so, dass es auch noch ein paar standardnachrichten gibt, die jedes interface beantworten können muss, und schon wär fooPacket überfordert. weiteres problem wäre natürlich, dass send teil eines interface ist,welches alle plugins unterstützen müssen, da ist man also schon gebunden.

    2. dein basetrick:
    1. ich steh ihm eh skeptisch gegenüber da man sobald eine neue klasse dazukommt direkt base mitverändern müsste.
    2. da ich zur compilezeit nicht einmal alle typen kenne, würde ich damit sehr große probleme bekommen^^



  • void send(Message message,Packet* packet
    

    das plugin kann also den datentyp in packet anhand der message indetifizieren.

    und hier verstehe ich nicht ganz, warum du 10 verschiedene
    messages verschickst, statt 10 verschiedene methoden aufzurufen.

    auch wenn die konkrete pluginklasse nicht verfügbar sein kann, so existiert
    doch zumindest ein plan, welche 10 verschiedenen messages diese pluginklasse
    essen kann. daher erbt diese pluginklasse von einer allen bekannten
    interfaceklasse, die genau diese 10 messages essen kann, und zwar als
    pur virtuelle methoden verpackt.

    das plugin kann also den datentyp in packet anhand der message indetifizieren.
    dh wenn zb in message "CreateShadowVolume" steht, kann ich davon ausgehen, dass in packet ein mesh und eine lichtquelle ist.

    oder dein plugin erbt von einer klasse, die die (pur) virtuelle methode CreateShadowVolume(Mesh*,LightSource*) hat. wäre doch auch angenehmer, die daten nicht immer vorher in ein paket verpacken zu müssen.
    ich verrate mal nicht, wie man auf angenehme weise pakete schnüren und entschnüren kann, die aus methodenzeiger und allen zugehörigen parametern bestehen.

    Dasselbe könnte ich aber dann auch mit void* pointern erreichen, vielleicht sogar schneller.

    ja, sogar schneller. aber da mußte unheimlich aufpassen, ne kleine fehlentscheidung und du bist langsamer. um in diesen sphären richtig zu entscheiden, muß man wissen, ob bei dem ziel-compiler methodenzeiger (wie üblich) 12 bytes groß sind bem dereferenzieren ein if ausführen, ob der zeiger (vptr) auf dir vtable am anfang oder am ende des objekts ist, wie man switch dazu bringt, die bereichüberprfung sein zu lassen und so weiter und so fort.
    da biste mit möglichst stilechtem c++ besser aufgehoben, weil es da keine teuren fehlentscheidungen gibt, mal angenommen sachen wie vector, dynamic_cast und so, die offensichtlich ihren preis haben, werden nur verwendet, wenn andere lösungen ähnlich teuer wären.

    es ist aber so, dass es auch noch ein paar standardnachrichten gibt, die jedes interface beantworten können muss, und schon wär fooPacket überfordert

    jedes interface muss ein paar standardnachrichten verarbeiten? klingt nach "jedes interface muß ein paar standardmethoden anbieten". genau dafür gibts den total geilen basisklassentrick.
    ich bin von deiner argumentation nicht überzeugt worden, es ist vielmehr so, daß ich langsam vermute, es ginge mit ganz normalen basisklassen und virtuellen methoden.

    2. dein basetrick:
    1. ich steh ihm eh skeptisch gegenüber da man sobald eine neue klasse dazukommt direkt base mitverändern müsste.
    2. da ich zur compilezeit nicht einmal alle typen kenne, würde ich damit sehr große probleme bekommen^^

    ok, das ist ein grund, den ich mal echt einsehe.
    also fummle ich es um:

    class Base{ 
       virtual char const* getClassName()=0;//nur pur, weil klasse eh abstrakt
       ... 
    }; 
    class Foo{ 
       virtual char const* getClassName(){return "Foo";} 
       ... 
    }; 
    ...
    

    ich sage aber nicht, daß du sowas verwenden sollst. ich vermute, daß dynamic_cast in flachen klassenhierarchien noch einigermaßen schnell ist. ausgemessen habe ich dyamic_cast nie, weil ich sowas nicht verwende.



  • Ich finde ja boost::any immer noch viel schöner als void* (und oft auch schöner als Klassenhierachien, im Plugin-Beispiel aber eher nicht). Mein Vorposter wird mir mal wieder den Schädel für Performance-Verschwendung abreißen und beim Plugin-System könnte es auch sehr gerechtfertigt sein, aber meine zwei void*-Probleme habe ich ganz schnell auf boost::any umgestellt und lebe jetzt sehr glücklich damit. (Eines davon war ein klassischer UserData-Parameter, den ich einem kleinen ListView-Control mit jedem Item gebe. Sehr praktisch.)

    C++ ist also nicht ganz machtlos, wo andere Sprachen sich auf ihre allgegenwärtige Object-Basisklasse berufen und void* ist wirklich kein Problem...



  • @volkard wenns so einfach wär^^
    normalerweise eine gute idee, aber mein projekt kann man als "experiment eines verrückten" bezeichnen.
    ich wollte mal aus lernzwecken versuchen(bzw bis jetzt nur aufm blatt papier), ein programm zu schreiben, dessen funktionalität komplett von den plugins abhängt,dh es besitzt keinen "core" bzw einen mainframe sondern nur plugins, die sich gegenseitig die informationen zuschieben und berechnen. Am ende soll irgendwas sinnvolles rauskommen^^.als beispiel ausgedrückt:
    eingabeplugin sendet daten zum berechnungsplugin, berechnungsplugin sendet daten zum ausgabeplugin.

    in dem experiment kenn ich nicht mehr von den plugins, als dass sie daten enpfangen und senden, was sie tun, und wie sie es tun, davon hab ich keine ahnung. Es ist wie gesagt nur ein experiment, und muss nicht unbedingt schnell sein oder am ende einen sinn haben, es soll nur funktionieren(und flexibel sein)^^

    aber des is doch schon ziemlich ot^^



  • otze schrieb:

    in dem experiment kenn ich nicht mehr von den plugins, als dass sie daten enpfangen und senden, was sie tun, und wie sie es tun, davon hab ich keine ahnung. Es ist wie gesagt nur ein experiment, und muss nicht unbedingt schnell sein oder am ende einen sinn haben, es soll nur funktionieren(und flexibel sein)^^

    aber der sender kennt seinen empfänger? das allein würde mir schon reichen.


Anmelden zum Antworten