by value ohne copy-constructor



  • Zwei newbie-Fragen:

    1. Wieso gibt der C++ Compiler keine "Warning" aus,
    wenn man eine Klasse als Argument an eine Funktion
    "by value" übergibt, wenn die Klasse keinen
    copy-konstruktor hat ??
    So etwas führt fast sicher zum Absturz des
    Compilats, und es handelt sich dabei doch meist
    um Versehen.

    2. Wenn am Anfang eines {....} Blocks
    mit new Speicher alloziert wird, ist dann
    sicher, daß das Compilat bei Erreichen von
    } oder return(..) den Speicher wieder
    freigibt, selbst ohne explizites delete ?
    Falls nicht:
    muß man dann wirklich vor jedem return-Aussprung
    die jeweils noch mit new allozierten Sachen
    "delete"-n oder kann man sich das irgendwie
    vereinfachen ?



  • 1.) Copy-Constructor hat jede Klasse. Wenn du keinen programmierst, wird selbst einer erzeugt der die Daten einfach kopiert.

    2.) std::auto_ptr, std::vector



  • zu 2.) alles was du mit new anlegst musst du auch mit delete löschen. Deshalb sollte man auch soweit möglich lokale Variablen bzw. mit den Attributen von Objekten arbeiten (deren Schnittstelle man idealerweise so definiert, dass sich die Objekte um das Erzeugen und Löschen selbst kümmern). Das Reduziert den Stress der durch das verwenden von new/delete ausgelöst wird erheblich.
    😉



  • Zu 1.) Auf den Default copy c'tor ist keinesfalls verlass. Man sollte entweder ausschliessen das er benutzt wird(im private Bereich deklarieren) oder ihn komplett selbst schreiben.



  • Danke für die Antworten.
    Also gut, um new/delete muß man sich selbst kümmern, das sehe ich ein.

    Aber was passiert mit new-alloziertem Speicher nach Programmende,
    also Rückkehr ins OS ?
    Werden dann alle allozierten Speicher freigegeben und alle Filehandles
    gelöst (auch evtl. noch nicht ge-close-te) oder kann es dabei zu
    Speicherlöchern kommen, wenn man mit exit() ins OS zurückkehrt ohne
    alle "new" mit "delete" gelöscht und ohne alle "open" mit "close"
    beendet zu haben ??



  • Wenn dein Programm komplett beendet wird, nimmt sich natürlich das OS den Speicher wieder zurück. Bleibt natürlich keine Speicherlücke. Wenn du etwas vergisst zu deleten, dann gibts die verlorene Speicherlücke nur solange dein Programm läuft, ist doch auch irgendwie logisch.



  • Mit new alloziertes, bleibt auf jeden Fall liegen. Erst beim Neustart ist der Speicher wieder Frei.



  • PunI$0R schrieb:

    Zu 1.) Auf den Default copy c'tor ist keinesfalls verlass. Man sollte entweder ausschliessen das er benutzt wird(im private Bereich deklarieren) oder ihn komplett selbst schreiben.

    Wäre mir neu.

    Schwierigkeiten mit dem Standardkopierkonstruktor gibts eigentlich nur dann wenn innerhalb eines Objekts Zeiger auf andere Objekte gehalten werden, die beim Kopieren des Objekts auch zu kopieren sind.
    Der Standardkopierkonstruktor geht immer davon aus, dass er nur die Adresse des Objekts zu kopieren hat und das ist in diesem Fall nicht richtig.



  • PunI$0R schrieb:

    Mit new alloziertes, bleibt auf jeden Fall liegen. Erst beim Neustart ist der Speicher wieder Frei.

    Ganz sicher nicht 🙄



  • n'UB schrieb:

    So etwas führt fast sicher zum Absturz des
    Compilats, und es handelt sich dabei doch meist
    um Versehen.

    Alles eine Sache des Programmierstils. Einfaches Beispiel: Wenn man als Member std::string statt char* und std::list<int> statt int* verwendet, hat man auf einmal keine Probleme mehr mit dem automatisch generierten Copy-Ctor. Übrigens muss man dann auch keinen Destruktor schreiben und im Falle einer Exception ist man auch fast automatisch auf der sicheren Seite.



  • delete braucht man gar nicht (oder ganz ganz selten) wegen std::auto_ptr, boost::shared_ptr und boost::scoped_ptr.

    new[] und delete[] braucht man gar nicht (oder ganz ganz selten) wegen std::vector und boost::array.



  • Ok, dann fasse ich das, was ich verstehe, mal zusammen:

    1. mit new alloziertes muß stets mit delete freigegeben werden, da muß man
      sich selbst drum kümmern.

    2. Verpaßte deletes und nicht geschlossene Files erzeugen zwar Speicherlücken
      während das Compilat läuft, aber *nach* Rückkehr ins OS ist der Speicher dann
      in jedem Fall
      wieder freigegeben, selbst wenn das Compilat durch einen exit() abgebrochen
      wurde und alle Files noch offen sowie alle new noch ohne delete sind

    3. der default copy-constuctor tut's nicht mehr, sobald im Konstruktor
      Speicher mit new angefordert wird, da dann durch die bitweise Kopie, die der
      copy constructor anlegt, beide Objekte denselben Speicher haben wollen oder
      ähnliche Probleme zu erwarten sind.

    ..? Ist das so richtig ?
    danke für die Antworten jedenfalls.
    Grüße



  • 2.) Das ist Sache des Betriebssystems und der Art der Resourcen und kann man deshalb nicht so allgemein beantworten.



  • n'UB schrieb:

    1. der default copy-constuctor tut's nicht mehr, sobald im Konstruktor
      Speicher mit new angefordert wird, da dann durch die bitweise Kopie, die der
      copy constructor anlegt, beide Objekte denselben Speicher haben wollen oder
      ähnliche Probleme zu erwarten sind.

    Der Default-Copy-Ctor erzeugt keine bitweise Kopie, sonst würden std::strings beim Kopieren ja auch kaputt gehen. Er ruft einfach den Copy-Ctor jedes Elements auf. BTW kann man auch Speicher mit new anfordern und dann z.B. einem boost::shared_ptr anvertrauen. Es kommt nur darauf an, dass man für die Freigabe und Kopierung eigene Klassen verwendet, nicht einfach rohe Zeiger (oder Handles oder...).



  • ""2.) Das ist Sache des Betriebssystems und der Art der Resourcen und kann man deshalb nicht so allgemein beantworten.""

    Das ist aber eben das Problem - wenn das Programm an irgendeiner
    tiefen Stelle, verschachtelt in 5 einander aufrufenden Funktionen,
    einen exit() machen muß, dann kann man doch vorher die GANZE LISTE
    belegter resourcen freigeben ????
    Dann bräuchte man doch direkt vor jedem exit() eine Routine, die
    Buch führt, was mit new und open belegt ist und dieses dann delete-t und
    close-t - das ist aber aufwendig, bei, sagen wir, 40 Stellen, wo
    exit() vorkommen kann, oder??
    Ist das wirklich zwingend, oder kann man sich nicht doch verlassen, dass
    das OS nach Beenden des Compilats noch offenen new freigibt ?



  • n'UB schrieb:

    Ist das wirklich zwingend, oder kann man sich nicht doch verlassen, dass
    das OS nach Beenden des Compilats noch offenen new freigibt ?

    Der werte void wollte damit sagen, das man die Frage nicht pauschal, sondern nur für jedes OS getrennt beantworten kann. Es gibt Betriebssysteme die lassen sich in die Resourcenverwaltung nicht so leicht reinreden, sprich die geben dann i.d.R. die meisten Resourcen des Programmes frei (Hinweis zu Ausnahmen folgt) und es gibt Betriebssysteme, bei denen "pfuscht" die z.B. Runtime des Programmes live in der Speicherverwaltung rum, und wenn es am Ende nicht aufräumt, kann es das OS auch nicht mehr erkennen (weil belegter Speicher dort halt keinem spezifischen Programm zugeordnet ist). Darum ist es unter der Betrachtungsweise portabler Entwicklung besser alles selber aufzuräumen. Das erfordert zwar etwas mehr Vorsicht (z.B. im Zusammenhang mit Exceptions) aber ist trotzdem keine schlechte Devise. 😉

    Zu den Ausnahmen selbst bei den "sorgfältigen" Systemen gehören Dinge wie Systemweite Resourcen die die Laufzeit überleben sollen, ein Beispiel wäre Shared Memory, den man i.d.R. so anlegt, das er das Programmende überlebt.

    Auf diese Frage gibt es also keine einfache Ja/Nein-Antwort. 🤡



  • n'UB......exit() benutzt man nicht in C++.



  • ......... schrieb:

    delete braucht man gar nicht (oder ganz ganz selten) wegen std::auto_ptr, boost::shared_ptr und boost::scoped_ptr.

    new[] und delete[] braucht man gar nicht (oder ganz ganz selten) wegen std::vector und boost::array.

    Naja,das ist IMHO eine eher gewagte Aussage.Über die boost Smart Pointer kann ich mir kein Urteil erlauben aber im Allgemeinen behaupte ich mal dass man mit Smart Pointern einigen Problemen aus dem Weg gehen kann um sich dafür aber neue zu schaffen(für den Interessierten z.B. hier nachzulesen http://www.aristeia.com/publications_frames.html.Die Artikel Smart Pointer 1-3).

    Bietet boost wirklich den generischen eierlegenden Wollmilchsau-Smart Pointer?

    MfG Spacelord



  • Ich kann dir zumindest rein empirisch sagen, dass ich bald mein MB an Quellcode voll habe, was das aktuelle Projekt angeht. Das enthält höchstens zwei bis drei deletes, genau so viele selbst geschriebene Copy-Konstruktoren - und wo stecken die? Richtig: In kleinen, praktischen Template-Klassen, die auch gar keine andere Aufgabe haben, als zu deleten. Der "normale" Anwendungscode kümmert sich derweil um die wichtigen Dinge, und das mit einer sehr geringen Dichte an Speicherfehlern. (Wenn ich mal eine Referenz in einen Container zu lange am Leben halte o.Ä.)



  • Das enthält höchstens zwei bis drei deletes, genau so viele selbst geschriebene Copy-Konstruktoren - und wo stecken die?

    bisschen viel, meinst du nicht? 😡



  • wütend schrieb:

    Das enthält höchstens zwei bis drei deletes, genau so viele selbst geschriebene Copy-Konstruktoren - und wo stecken die?

    bisschen viel, meinst du nicht? 😡

    Du scheinst dann ja genau der richtige zu sein der mir erklären kann wie der ultimative Smart Pointer aussieht,der sich also 100%(!) wie ein normaler Pointer verhält aber jegliches Speichermanagement übernimmt.

    Also,wie sieht der aus?
    Oder bist du nur ein Dumm-Schwätzer?
    Lass mal sehen wütend.

    MfG Spacelord


Anmelden zum Antworten