Zum Inhalt springen

Nach Löschen Zugriff auf Instanzmethoden


Wodar Hospur

Empfohlene Beiträge

Hi, beim Versuch einem Kommilitonen zu erklären wieso man nachdem Löschen von Zeigern diese auf 0 setzen soll bin ich auf etwas mir Unerklärbares gestoßen:


#include <iostream>

#include <string>


using namespace std;


class Bla

{

    public:

        string text;

        Bla(void)

        {

            text = string("Hallo");

        }

        void ausgeben(void)

        {

            cout << "Hallo" << "\n";

        }

}


int main(void)

{

    Bla* blubb = new Bla();


    blubb->ausgeben();


    delete blubb;

    blubb = NULL;


    blubb->ausgeben();


    return 0;

}

Liefert mir auf einem aktuellen Archlinux mit gcc 4.4.2 als Ergebnis: Hallo Hallo Wenn der Code jetzt auf:

#include <iostream>

#include <string>


using namespace std;


class Bla

{

    public:

        string text;

        Bla(void)

        {

            text = string("Hallo");

        }

        void ausgeben(void)

        {

            cout << text << "\n";

        }

}


int main(void)

{

    Bla* blubb = new Bla();


    blubb->ausgeben();


    delete blubb;

    blubb = NULL;


    blubb->ausgeben();


    return 0;

}

geändert wird gibt es den erwarteten und gewünschten segfault. Aber warum nicht vorher. Ich geh davon aus das es undefiniertes Verhalten ist. Wie kann man sonst diesen segfault provozieren, ist ja besser als in fremden Speicher reinzuschreiben, reinzuarbeiten.

Bearbeitet von Wodar Hospur
Link zu diesem Kommentar
Auf anderen Seiten teilen

geändert wird gibt es den erwarteten und gewünschten segfault. Aber warum nicht vorher. Ich geh davon aus das es undefiniertes Verhalten ist.
Es ist undefiniertes Verhalten. Du kannst hier schlicht und einfach nicht erwarten, dass etwas bestimmtes passiert. Sich zu fragen, warum Code mit "undefined Behavior" in diesem oder jenem Fall dieses oder jenes Verhalten zeigt, ist sinnlos.

Wie kann man sonst diesen segfault provozieren, ist ja besser als in fremden Speicher reinzuschreiben, reinzuarbeiten.
Sorg dafür, dass so etwas gar nicht erst passieren kann. Wenn du denn unbedingt mit Zeigern arbeiten musst, dann nimm Smartpointer.

beim Versuch einem Kommilitonen zu erklären wieso man nachdem Löschen von Zeigern diese auf 0 setzen soll
Das darfst du mir aber bitte auch mal erklären. Mir fällt dafür nämlich kein vernünftiger Grund ein. ;)
Link zu diesem Kommentar
Auf anderen Seiten teilen

Es ist undefiniertes Verhalten. Du kannst hier schlicht und einfach nicht erwarten, dass etwas bestimmtes passiert. Sich zu fragen, warum Code mit "undefined Behavior" in diesem oder jenem Fall dieses oder jenes Verhalten zeigt, ist sinnlos.

Naja wenn ich nen Zeiger auf Null setze sollte das einfach nicht passieren. Ich frag mich sogar eher warum es undefiniertes Verhalten gibt. Er müsste in beiden Fällen einfach sterben. Für mich sieht das irgendwie aus als ob die Funktion static ist und er dann drauf zugreifen würde...

Sorg dafür, dass so etwas gar nicht erst passieren kann. Wenn du denn unbedingt mit Zeigern arbeiten musst, dann nimm Smartpointer.

Naja aber in einer normalen Einführungsvorlesung ist die boost Lib einfach nicht Bestandteil.

Das darfst du mir aber bitte auch mal erklären. Mir fällt dafür nämlich kein vernünftiger Grund ein. ;)

Double Free. Sicher ein Problem das mit einer gescheiten Strukturierung zu lösen ist. Aber ein delete auf einen NULL Pointer macht nichts, während ein delete auf eine ungültige Adresse zu nem Absturz oder vielleicht auch nicht definiertem Verhalten führt. Außerdem ist der NULL Pointer bei jeglichen Graphen, Bäumen und Listen ein wichtiger Bestandteil.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Naja wenn ich nen Zeiger auf Null setze sollte das einfach nicht passieren. Ich frag mich sogar eher warum es undefiniertes Verhalten gibt.
Das Grundkonzept der Sprache ist, dass du für nichts mit Performance bezahlen musst, was du nicht brauchst. Man kann eine Programmiersprache so schreiben, dass bei Zugriffen über Nullzeiger grundsätzlich Laufzeitfehler ausgelöst werden (siehe Java), aber das erfordert eine Prüfung bei jedem einzelnen Zeigerzugriff, das kostet Performance und passt daher nicht ins Sprachkonzept. Wenn du so ein Feature brauchst, kannst du es dir selbst bauen.

Er müsste in beiden Fällen einfach sterben. Für mich sieht das irgendwie aus als ob die Funktion static ist und er dann drauf zugreifen würde...
Wie gesagt, es ist vom Standpunkt des Standards sinnlos, sich darüber Gedanken zu machen, warum sich dieser Code so oder so verhält.

Konkret ist es hier so, dass der this-Zeiger des Objekts als versteckter Parameter übergeben wird, und in diesem Fall eben Null ist. Das geht bei den meisten Compilern so lange gut, wie du nicht auf Membervariablen der Klasse zugreifst, denn nur deren Adressen beziehen sich auf diesen Zeiger.

Naja aber in einer normalen Einführungsvorlesung ist die boost Lib einfach nicht Bestandteil.
Es muss ja nicht gleich boost sein, einfache Smartpointer kann man sich auch selbst stricken. Und std::auto_ptr gibt's auch noch, auch wenn der ein ziemlich eingeschränktes Anwendungsfeld hat.

Double Free. Sicher ein Problem das mit einer gescheiten Strukturierung zu lösen ist.
So sehe ich das auch. Ein doppeltes delete ist ein Fehler, den du mit dem Nullsetzen nur vertuschst. Quasi eine Symptombehandlung.

Aber ein delete auf einen NULL Pointer macht nichts, während ein delete auf eine ungültige Adresse zu nem Absturz oder vielleicht auch nicht definiertem Verhalten führt.
Letzteres.

Außerdem ist der NULL Pointer bei jeglichen Graphen, Bäumen und Listen ein wichtiger Bestandteil.
Richtig. Aber mal ehrlich, wie oft implementiert man solche Container selbst?
Link zu diesem Kommentar
Auf anderen Seiten teilen

Das Grundkonzept der Sprache ist, dass du für nichts mit Performance bezahlen musst, was du nicht brauchst. Man kann eine Programmiersprache so schreiben, dass bei Zugriffen über Nullzeiger grundsätzlich Laufzeitfehler ausgelöst werden (siehe Java), aber das erfordert eine Prüfung bei jedem einzelnen Zeigerzugriff, das kostet Performance und passt daher nicht ins Sprachkonzept. Wenn du so ein Feature brauchst, kannst du es dir selbst bauen.

Joah es geht mir weniger nach dem warum sondern eher nach dem wie.. woher er also das noch auflösen kann. Aber das hast du ja in einem anderen Absatz dann erklärt.

Konkret ist es hier so, dass der this-Zeiger des Objekts als versteckter Parameter übergeben wird, und in diesem Fall eben Null ist. Das geht bei den meisten Compilern so lange gut, wie du nicht auf Membervariablen der Klasse zugreifst, denn nur deren Adressen beziehen sich auf diesen Zeiger.

Genau das find ich interessant. Das heißt mit dem Typen alleine sind alle Methoden verknüpft. Das sind halt Sprachinternas die garnich so leicht ersichtlich sind.

Richtig. Aber mal ehrlich, wie oft implementiert man solche Container selbst?

Fast ein halbes Semester lang ;).

Link zu diesem Kommentar
Auf anderen Seiten teilen

Genau das find ich interessant. Das heißt mit dem Typen alleine sind alle Methoden verknüpft.

Es geht übrigens in aller Regel nicht mehr, wenn du virtuelle Methoden aufrufst. Denn die sind üblicherweise über einen vtable-Zeiger implementiert, der an der Instanz hängt, also auch relativ zum Instanzzeiger adressiert wird.

Das sind halt Sprachinternas die garnich so leicht ersichtlich sind.
Es handelt sich weniger um Sprachinterna als um Implementierungsdetails der Compiler. Du bist hier längst über den Bereich hinaus, der durch die Sprache geregelt wird.
Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 2 Wochen später...

Das ganze ist einfach ganz logisch zu erklären.

Nachdem dein Programm in Maschienencode umgewandelt (compiliert) wurde, gibt es keine klassen mehr.

aus


class Bla

{

    public:

        void ausgeben(void)

        {

            cout << text << "\n";

        }

}

wird

void ausgeben(void* this)

{

     cout << this->text << "\n";

}

und aus

blubb->ausgeben();

wird

ausgeben(blubb);

ist logisch das bei folgendem kein Fehler entsteht, egal was du übergibts, oder?

void ausgeben(void* this)

{

     cout << "Hallo" << "\n";

}

Das ist kein Fehler, ds ist einfach so ;-)

Im Regelfall wird es aber selten vorkommen, dass deine Klasse über Methoden verfügt, die nicht auf den Klassenspeicher zugreifen.

HTH,

Uwe

Link zu diesem Kommentar
Auf anderen Seiten teilen

Dein Kommentar

Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung wiederherstellen

  Nur 75 Emojis sind erlaubt.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Editor leeren

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

Fachinformatiker.de, 2024 by SE Internet Services

fidelogo_small.png

Schicke uns eine Nachricht!

Fachinformatiker.de ist die größte IT-Community
rund um Ausbildung, Job, Weiterbildung für IT-Fachkräfte.

Fachinformatiker.de App

Download on the App Store
Get it on Google Play

Kontakt

Hier werben?
Oder sende eine E-Mail an

Social media u. feeds

Jobboard für Fachinformatiker und IT-Fachkräfte

×
×
  • Neu erstellen...