Methodenaufrufe, ohne dass zuvor ein Obj angelegt wird.


  • Administrator

    Es ist schlicht und einfach undefiniertes Verhalten. Die Welt könnte explodieren, deine Festplatte gelöscht werden oder Microsoft pleite gehen. Es kann funktionieren, weil es irgendwie grad reinpasst, aber du kannst dich nie im Leben darauf verlassen.
    Wenn du eine nicht statische Methode aufrufst, brauchst du ein gültiges Objekt dazu. Sonst ist es eben undefiniert.

    Grüssli



  • Dravere schrieb:

    Wenn du eine nicht statische Methode aufrufst, brauchst du ein gültiges Objekt dazu. Sonst ist es eben undefiniert.

    Gilt das auch für PODs?



  • Mitleid schrieb:

    Dravere schrieb:

    Wenn du eine nicht statische Methode aufrufst, brauchst du ein gültiges Objekt dazu. Sonst ist es eben undefiniert.

    Gilt das auch für PODs?

    Das ist eine gute Frage. Im Zweifelsfall würde ich aber sagen: ja.

    Bzw. die noch bessere Frage ist eigentlich: muss man ein Objekt vom Typ T an einer bestimmten Adresse konstruiert haben, damit es OK ist, mit dieser Adresse als "this" Pointer, eine bestimmte Member-Funktion T::F() aufzurufen, wenn T ein POD ist 🙂


  • Administrator

    Gegenfrage:
    Wieso sollte das nicht auch für PODs gelten? Zugriff auf Speicher, welcher dir nicht gehört, ist undefiniert. Oder auf was wollt ihr hinaus?

    Grüssli



  • Dravere schrieb:

    Gegenfrage:
    Wieso sollte das nicht auch für PODs gelten? Zugriff auf Speicher, welcher dir nicht gehört, ist undefiniert. Oder auf was wollt ihr hinaus?

    Grüssli

    Ich denke nicht an Speicher der nicht mir gehört. Ich denke an sowas:

    struct POD
    {
        int value;
        void F() {}
    };
    
    void test()
    {
        void* mem = new char[sizeof(POD)];
    
        POD* pod = static_cast<POD*>(mem);
    
        pod->value = 123; // OK
        pod->F(); // OK oder UB???
    
        delete [] static_cast<char*>(mem);
    }
    

    Ich schätze aber fast, dass es in diesem Beispiel OK sein müsste. Schätze. Nix wissen. 🙂
    (Nicht dass ich vor hätte solchen Code zu schreiben, ist mir noch nie was untergekommen wo ich sowas hätte brauchen können)

    p.S.: worauf der OP hinaus will, weiss ich nicht. Vermutlich auf grossen Unfug 😃



  • Ist denn this ein POD?



  • Tachyon schrieb:

    Ist denn this ein POD?

    😕
    Was für ein this ?
    Blub?
    Nix versteh...



  • hustbaer schrieb:

    Tachyon schrieb:

    Ist denn this ein POD?

    😕
    Was für ein this ?
    Blub?
    Nix versteh...

    Ich nehme mal an er meint: "Ist den this für ein POD definiert?"
    (Alleine schon durch die Rahmenbedingungen die für ein POD existieren, wage ich das zu bezweifeln)

    cu André



  • asc schrieb:

    ...

    Dann ist dies...

    struct NoPODQuestionmark
    {
        int a;
        void f()
        {
            this->a = 10;
        }
    };
    

    ...kein POD mehr, weil man in NoPODQuestionmark::f() this dereferenziert?



  • Natürlich haben PODs einen this-Pointer.
    Auf was ihr immer für Ideen kommt... 😕



  • hustbaer schrieb:

    Natürlich haben PODs einen this-Pointer.
    Auf was ihr immer für Ideen kommt... 😕

    Ja, nur dass der anders hergeleitet wird, als bei nicht-POD-Typen. Daher ist Dein Beispiel von oben auch kein UB.



  • Tachyon schrieb:

    ...kein POD mehr, weil man in NoPODQuestionmark::f() this dereferenziert?

    Ich musste mir gerade nochmal die POD-Definition durchlesen, du hast recht. Ich habe so selten mit POD zu tun, das mir nicht bewusst war das nicht-statische Methoden möglich sind.


  • Administrator

    hustbaer schrieb:

    Ich denke nicht an Speicher der nicht mir gehört. Ich denke an sowas: ...

    Nicht ganz das, was der Threadersteller macht 😉

    Aber so, hmmmm ...
    Theoretisch, wäre sowas ja nichts anderes als:

    char arr[sizeof(int)];
    int* p = reinterpret_cast<int*>(&arr[0]);
    *p = 10;
    

    Und sowas ist doch erlaubt? Daher wäre es wahrscheinlich erlaubt ...
    Ich würde es aber trotzdem über ein Placement New machen 🙂

    Grüssli



  • Gestützt auf §3.8 würde ich sagen, dass es bei einem POD reicht den Speicher zu reservieren und das Objekt ist gültig, daher ist der Funktionsaufruf auch kein UB. Für einen "non-POD class type" wird der Aufruf einer nicht-statischen Funktion ja explizit als UB definiert (auch §3.8).

    Aber helft mir mal kurz.

    Wenn ich, wie der Threadstarter, einen mit 0 initialisierten Zeiger nehme und über diesen eine nicht-statische Funktion aufrufe die selbst keine illegalen Speicherzugriffe tätigt, wo sagt mir der Standard, dass das UB ist?

    Von der Anschauung her würde ich sagen, dass das klappen müsste, schließlich ist es dem Funktionsaufruf selbst ja egal ob der übergebene this-Zeiger auf gültigen Speicher zeigt oder nicht, solange er nicht dereferenziert werden muss.

    Ist die Funktion virtuell, dann ist Ende, das ist klar, aber was übersehe ich da bei einem normalen Funktionsaufruf bzw. wo ist die entsprechende Stelle im Standard?

    Nicht dass ich irgendetwas in der Richtung implementiert habe, nur aus Interesse. 😃

    P.S.:

    hustbaer schrieb:

    Bzw. die noch bessere Frage ist eigentlich: ... formaljuristisch korrekter Ausdruck ...

    So hab ich's gemeint. :p 😃


  • Administrator

    Gut, ich helfe dir mal. Ich habe mal "kurz" im Standard gelesen und bin dabei auf das hier gestosssen. Ich glaube das definiert das Problem 🙂

    Standard ISO IEC 14882:2003 schrieb:

    5.2.5 Class member access

    1 A postfix expression followed by a dot . or an arrow ->, optionally followed by the keyword template
    (14.8.1), and then followed by an id-expression, is a postfix expression. The postfix expression before the
    dot or arrow is evaluated; the result of that evaluation, together with the id-expression, determine the
    result of the entire postfix expression.

    2 For the first option (dot) the type of the first expression (the object expression) shall be “class object” (of a
    complete type
    ). For the second option (arrow) the type of the first expression (the pointer expression) shall
    be “pointer to class object” (of a complete type). In these cases, the id-expression shall name a member of
    the class or of one of its base classes. [Note: because the name of a class is inserted in its class scope
    (clause 9), the name of a class is also considered a nested member of that class. ] [Note: 3.4.5 describes
    how names are looked up after the . and -> operators. ]

    3 If E1 has the type “pointer to class X,” then the expression E1->E2 is converted to the equivalent form
    (*(E1)).E2;
    the remainder of 5.2.5 will address only the first option (dot). Abbreviating object-
    expression.id-expression as E1.E2, then the type and lvalue properties of this expression are determined as
    follows. In the remainder of 5.2.5, cq represents either const or the absence of const; vq represents
    either volatile or the absence of volatile. cv represents an arbitrary set of cv-qualifiers, as defined
    in 3.9.3.

    Somit muss also das Objekt gültig sein und beim Zugriff über einen Zeiger wird dieser zuerst dereferenziert. Das dereferenzieren einer ungültigen Speicheradresse ist undefiniert.

    Oder habe ich da einen Denkfehler gemacht? 🙂

    Grüssli



  • Dravere schrieb:

    Gut, ich helfe dir mal.

    Das ist aber nett. Vielen Dank. 🙂

    Fazit: Besser gleich auf Tippgeber gehört, der da meinte es hat keinen Sinn darüber zu diskutieren und Recht hatte er. 😃



  • @Dravere:
    Die Stelle sagt IMO über das hier Diskutierte nichts aus.

    Ich denke du misverstehst den Begriff "complete type".
    Complete Type bedeutet dass ein Typ (Klasse) definiert ist, und nicht nur deklariert:

    struct A;
    
    // <- A ist "incomplete" hier
    
    struct A
    {
       int i;
    };
    
    // <- hier ist A jetzt ein "complete type"
    

  • Administrator

    Jap, hab grad nochmals nachgelesen, "complete type" bedeutet hier was anders. Aber was ist mit Abschnitt 3? Ein Aufruf der Form p->methode() wird umgewandelt zu (*(p)).methode() . Eine Dereferenzierung von p , wenn p auf 0 oder einem Speicherbereich zeigt, welcher einem nicht gehört, führ doch zu UB.
    Oder versteh ich das auch falsch? ...

    Grüssli



  • Ihr habt doch hier zwei unterschiedliche Situationen. Eine, in der das Objekt gültig ist, und eine, in der das Objekt nicht gültig ist. Dabei dürfte doch völlig egal sein, ob das Objekt POD oder nicht POD ist. Nur, dass bei PODs das Objekt bereits gültig ist, sobald der Speicher reserviert wurde.

    NonPodType* npt = 0;
    npt->feld = 10; // UB
    npt->methode(); // UB
    
    PodType* pt = 0xdeadbeef;
    pt->feld = 10; // UB
    pt->methode(); // UB
    
    NonPodType* npt2 = new NonPodType();
    npt2->feld = 10; // ok
    npt2->methode(); // ok
    
    PodType* pt2 = (PodType*) malloc(sizeof(PodType)); // man verzeihe mir das C-gefrickel
    pt2->feld = 10; // ok
    pt2->methode(); // ok
    


  • @LordJaxom: richtig.


Anmelden zum Antworten