Wie von Klassenreferenz nach Pointer oder umgekehrt ohne Nebeneffekte und Memory-Leaks?

  • Um eine Klasse zu instanzieren gibt es zwei Möglichkeiten:


    Code
    MyClass meine_instanz;


    oder


    Code
    MyClass *meine_instanz = new MyClass;


    Im ersten Fall (Referenz) kümmert sich der Compiler darum den Speicher an der richtigen Stelle freizugeben. Im zweiten muss ich selber mit "delete" den Speicher freigeben. Zumindest habe ich das so verstanden.


    Was aber nun wenn ich sauber und ohne Nachteile/Nebeneffekte vom einen in das andere wandeln will, bzw. aus einer Funktion ggf. beide "Gebiete" bedienen kann?


    Wenn meine Funktion einen Pointer (erzeugt mit new) zurückgibt, dann bekomme ich daraus zwar wieder eine Klassen-Referenz, wenn man aber dieser Antwort glauben darf, dann ist das eine Kopie. Ich muss mich also doch noch darum kümmern den ursprünglichen Pointer wieder freizugeben:


    http://stackoverflow.com/quest…rence-new-pointer#tab-top


    Die Idee ist aber den ursprünglichen Pointer eben nicht mehr zu brauchen. Ich will ja ohne Nebeneffekte von der "einen Welt" (erzeugt als Pointer) in die andere (erzeugt als Referenz) umwandeln.


    Was ist mit dem umgekehrten Weg? Was, wenn ich einen Pointer auf eine Klassen-Referenz mit "&" erzeuge? Bekomme ich so das gleiche als hätte ich "new" genutzt? Soll heißen: Bleibt die Klasseninstanz dann so lange stehen bis ich meinen Pointer mit "delete" wegräume? Oder bekomme ich wieder eine Kopie?


    Kann hier irgendwer helfen und ggf. auch einen Link liefern wo das mal etwas einfacher beschrieben wird?

  • Es geht hier nicht so sehr um Pointer und Referenz, sondern Heap und Stack. "MyClass" ist keine Referenz, sondern "MyClass&" ist eine. Das ist dann kein neues Objekt, sondern praktisch das gleiche wie ein Pointer, nur dass du mit . auf die Elemente zugreifst und nicht mit ->.


    Mit new erzeugst du Objekte auf dem Heap, die immer mit delete gelöscht werden müssen.
    Das andere ist ein Objekt auf dem Stack. Wenn man davon genug erstellt oder diese Objekte sehr groß sind, kommt irgendwann der stack overflow.


    Wenn du Objekte auf dem Heap erzeugen möchtest, dich aber nicht um das delete kümmern willst, kannst du ein C++11 Feature nutzen: shared_ptr
    http://en.m.wikipedia.org/wiki…r#shared_ptr_and_weak_ptr


    Letztendlich spielt es keine Rolle, ob Pointer oder Referenz, sondern nur, wie lange das Objekt leben soll. Objekte auf dem Stack leben nur so lange, wie der Gültigkeitsbereich existiert. Gibt's du einen Pointer oder eine Referenz auf ein Stackobjekt irgendwo hin und wird dieser Stackframe beendet, ist das Objekt weg und dein Zeiger zeigt auf ungültigen Speicher; es kommt dann beim Zugriff zu einem segfault.


    Erzähle etwas mehr von dem Problem, das du hast, dann lässt sich sowas leichter erklären.
    Im Gegensatz zu den Sprachen mit garbage collector muss man bei C/C++ etwas mehr nachdenken, wenn es um alloziierten Speicher geht. Die GC-Sprachen machen quasi immer Objekte auf dem Heap, die mit sowas wie shared_ptr verwaltet werden. Wenn keiner mehr den Pointer kennt, wird das Objekt zerstört.


    Lars.

  • Danke für die Erklärung. Das macht die Hintergründe schon verständlicher. Heißt wohl: Pointer auf Klassen nicht mit "new" sondern mit "&" zu machen ist eine blöde Idee weil man so potentiell SegFaults bekommen kann.


    Was das konkrete Problem angeht: Ich habe hier mit Code zu tun der beides nutzt und war bisher nicht ganz schlüssig mit welchem Weg ich am einfachsten ans Ziel komme.


    Scheint mir aber so als wäre in meinem Fall die "new" und "delete"-Lösung die bessere. Damit weiß ich wenigstens sicher wie lange mein Objekt gültig bleibt. Im VDR-Source habe ich auch mehrere solche Fälle gefunden. Dann muss ich mich halt selber darum kümmern zum richtigen Zeitpunkt ein "delete" aufzurufen.


    Ist aber schon verdammt kompliziert, dieses C++. Schon was man beim Klassendesign alles beachten muss. "virtual"-Destruktoren in Klassen und wasweißichnichtalles. Wäre ja fast ein Wunder wenn ich bei meinen ersten Schritten nicht irgendwo "Speicher verliere" ;)

  • shared_ptr sind eine nette Sache. Benutzen wir in der Firma schon länger. Leider können wir den C++11 nicht nutzen wegen iOS :(.
    Deshalb nehmen wir direkt gleich boost.


    Gerald


    HP Proliant MicroServer Gen8, Xeon E3-1230, 12 GB RAM, 3xWD red 2TB im RAID 5, 2xSundtek MediaTV Home DVB-C/T, L4M TWIN-C/T, Ubuntu Server 14.04.1, Plex Media Server
    Samsung UE55H6470

  • Man kann natürlich auch mit class templates arbeiten, so etwa wie in der Win32 COM Programmierung.


  • Bitte keine:

    Code
    if (x) delete x;


    http://stackoverflow.com/quest…-to-delete-a-null-pointer
    http://en.wikipedia.org/wiki/Delete_%28C%2B%2B%29


    Selbe gilt für free:

    Code
    if (x) free (x)


    bitte nicht verwenden!


    Johns

    Sag mir, wo die Developer sind. Wo sind sie geblieben? . . . . . . . . . . . . . . . . . . . . SoftHdDevice - A software and GPU emulated HD output device plugin.
    Sag mir, wo die Developer sind. Was ist geschehn?


    Client0: Crown CW02 MSI_C847MS-E33 Zotac_GT640_passiv Cine-S2 iMon-MCE / streamdev softhddevice
    Client1: Lian_Li_PC-Q09FB ASRock_H67M-ITX/HT I3-2100 ASUS_ENGT520_passiv / streamdev softhddevice
    Test: Lian_Li_PC-Q09R Asus C60M1-I / streamdev
    Server0: Dockstar TT-S2-3600-USB / streamdev
    Server2: Lian_Li_PC-Q07R Intel_DH61DL G620 WD20EARX 90W PicoPSU Cine-S2+DuoFlex-S2+DuoFlex-CT / streamdev / 22 Watt Verbrauch

  • "virtual"-Destruktoren in Klassen und wasweißichnichtalles.


    Das deterministische Aufrufen der Destruktoren ist das, was mir bei den GC-Sprachen am meisten fehlt. Da kann man immer so schön hinter sich aufräumen. Ansonsten muss man immer mit Dispose-Pattern u.ä. rumärgern. Geht natürlich auch, ist aber etwas gewöhnungsbedürftig.


    Wäre ja fast ein Wunder wenn ich bei meinen ersten Schritten nicht irgendwo "Speicher verliere" ;)


    Das tut jeder und nicht nur am Anfang seiner Laufbahn. :)


    Ob man nun Objekte mit new oder per Copy-Konstruktor zurückgibt, hängt maßgeblich auch von den Objekten selbst und deren Größe bzw. den Performance-Ansprüchen ab. Bei kleinen Objekten mit ein paar einfachen Feldern (z.B. int o.ä.), muss es nicht zwingend new sein:


    Bedenken muss man hier, das insgesamt zwei MySize-Objekte erstellt werden. Das innerhalb von GetSize, welches dann mit Hilfe des (in diesem Fall automatisch erzeugten) Copy-Konstruktor in das Objekt s kopiert wird. Das erste Objekt wird dann gleich wieder freigegeben. Und das ist die größte Fallgrube. Wenn MySize jetzt ein Member hätte, das z.B. ein Pointer auf dynamischen Speicher wäre und dieser würde im Destruktor freigegeben werden, dann hätte s ein Problem, weil es einen Pointer hätte, der auf schon freigegebenen Speicher zeigt. Deshalb muss man in so einem Fall einen eigenen Copy-Konstruktor schreiben, der im neuen Objekt Speicher alloziiert und ihn mit dem Inhalt des alten Objektes füllt.
    Typisches Beispiel sind String-Klassen, auch im vdr (nur das Klaus die C-str-Routinen für Strings benutzt, statt "new char[...]" bzw. std::string):
    http://projects.vdr-developer.…vdr.git/tree/tools.h#n168
    http://projects.vdr-developer.…dr.git/tree/tools.c#n1036


    Ein Gefühl kann man dafür kriegen, indem man ein bisschen damit rumspielt und ein kleines, eigenes Reference-Counting implementiert:
    (EDIT: das ist natürlich kein reference counting, jedem Objekt wird nur zu Loggingzwecken eine eindeutige ID zugeteilt und es muss wahrscheinlich noch ein Copy-Konstruktor geschrieben werden, der nicht ID vom zum kopierenden Objekt holt)


    Keine Sorge, wenn du dir ins eigene Knie schießt - das tun wir alle.
    Das wichtigste ist, genau festzulegen, wer die Verantwortung über den Pointer hat, wenn du ein Objekt mit new erstellst.


    (jeglichen Code habe ich nicht kompiliert, sondern einfach nur so hingeschrieben)


    Lars.

    vdr2: yaVDR 0.5/softhddevice @ G540, Intel DH67BLB3, Asus GT610/2GB, DDBridge + 2x DuoFlex C/T
    hdvdr: yaVDR unstable/softhddevice @ E8400, Asus P5Q SE Plus, 1x L4M-TwinCI + Flex C/T, 1x Sundtek MediaTV Pro, GT520
    Plugins: | avahi4vdr | dbus2vdr | dynamite | epg2timer | noepg | pvrinput | sundtek |

    Einmal editiert, zuletzt von mini73 () aus folgendem Grund: Copy-Konstruktor zu RefCounter hinzugefügt.


  • Bei etwas nachlesen.. berechtigt. Danke. :)

  • Ist aber schon verdammt kompliziert, dieses C++. Schon was man beim Klassendesign alles beachten muss.


    Der Vorteil ist, dass du die komplette Kontrolle hast.
    Der Nachteil ist, dass du dein Programm komplett kontrollieren musst. :)


    Wenn man C++ erst mal verinnerlicht hat, sind die "modernen" GC-Sprachen alle um so einfacher.


    Lars.

Jetzt mitmachen!

Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!