Autor | Beitrag |
---|
000 11.08.2010, 15:48 ElChupkapres |
hi, ich habe eine Frage bzw. ein Problem bezüglich des löschens von dynamisch erstellten Arrays. Ich hab eine Klasse C, der ein Array und die größe des Arrays übergeben wird. Der Konstruktor erstellt ein dynamisches Array mit der entsprechenden Größe und kopiert die Werte des Arrays. (Hab den Code sehr minimal gehalten wegen der Übersicht)
Daher die Fragen: Wie gebe ich jetzt am besten den Speicherplatz wieder frei, wenn ich das nicht über den Destruktor mache. Wenn ich es mit Destruktor mache kann ich später nicht mehr auf meine Instanzen von C zugreifen. Ich könnte zwar eine delete Methode in der Klasse C implementieren, aber dann müsste man die jedensmal aufrufen bevor der Vector gelert wird, das kann man leicht vergessen. Bei den Beispielen die man im Netz findet, wird auf ein Objekt mit dynamischen Array immer nur innerhalb des Scopes auf das Objekt zugegriffen, daher habe ich leider bisher keine passende Lösung gefunden. Hoffe ihr könnt mir ein paar Tipps geben. -- |
Profil || Suche |
001 11.08.2010, 16:32 Bluthund |
Du hantierst in Klasse C manuell mit Speicher herum aber hast weder den Kopierkonstruktor noch operator=() ueberschrieben. Das bedeutet, dass dein Pointer auf den geholten Speicher bei einem Kopiervorgang der Klasseninstanzen (was u.a. beim Einfuegen von Objekten in eine STL-Container-Klasse geschieht) einfach 1:1 in die neue Instanz kopiert wird. Du hast also nach dem Kopieren zwei Instanzen, deren m_array-Pointer beide auf das selbe Array im Speicher zeigen. Wenn du dann delete[] auf diesem Array in einer der beiden Instanzen aufrufst zeigt der Pointer der anderen nach wie vor an diese Stelle. Wenn du dann versuchst ueber diesen Pointer zu lesen oder zu schreiben kracht es verstaendlicherweise. Ausserdem: Warum arbeitest du in Klasse C ueberhaupt mit C-Style Arrays? Nimm einen vector<double>. Dann brauchst du dich auch nicht darum kuemmern, wie das Ding zu zerstoeren ist. Du brauchst nach einem delete im Desktruktor auch den Pointer nicht 0 zu setzen, da der Pointer nach dem Austritt aus der Funktion eh verschwindet wenn das Objekt abgebaut wird. --The C language combines all the power of assembly language with all the ease-of-use of assembly language. Dieser Beitrag wurde am 11.08.2010 um 16:41 von Bluthund bearbeitet. |
Profil || Suche |
002 11.08.2010, 17:20 Kriz |
Alle Vaterklassen sollten ihre Members grundsätzlich als protected deklarieren, damit Kindklassen direkt darauf zugreifen können und nicht umständlich über die öffentlichen Methoden der Vaterklasse. So wie Bluthund das beschrieben hat, machen das nur Noobs (wobei Bluthund keinesfall ein solcher ist). Private wird nur das deklariert, was die Vaterklasse angeht und sonst niemanden. Wer allerdings eh nicht ableitet, der sollte von vornherein die Members private deklarieren, da hat Bluthund recht. Und wer ganz qualvoll agieren möchte, der nutze protected/private Ableitungen... @ElChupkapres: Bei Zugriffen auf Standard-Templates wie vector<> solltest du dir langsam mal den Zugriff per Iteratoren aneignen. for-Schleifen sind in diesem Fall ausnahmsweise sehr unflexibel gegenüber Iteratoren. --K:R-I)Z++ |
Profil || Suche |
003 11.08.2010, 18:24 Bluthund |
Die einen nennen es umstaendlich und die anderen gute Kapselung (und die sollte nicht nur nach aussen sondern auch innerhalb einer OO-Architektur herrschen). Eine Kindklasse erbt sowohl Verhalten als auch Zustand von ihren Eltern. Wenn du den Zustand aber prinzipiell fahrlaessig an jedes Kind freigibst, ist es nur eine Frage der Zeit bis irgendwann mal eine Kindklassenimplementierung irgendeine Invariante einer ihrer Elternklassen verletzt, weil sie uneingeschraenkten Zugriff auf deren Zustand hatte, und damit das Verhalten der Implementierung der Elternklasse (und damit auch das der Kindklasse) voellig unvorhersehbar wird. Und das gilt bei Weitem nicht nur fuer Membervariablen sondern auch fuer non-const Memberfunktionen. Wenn es keine Invarianten gibt, die eine bestimmtes Basisklassenmember betreffen, spricht natuerlich nichts dagegen sie protected zu deklarieren, um einen einfachen Zugriff zu ermoeglichen. Wobei man sich hier natuerlich Gedanken machen sollte, ob das im Sinne der Konsistenz des Klasseninterfaces ist. Neulinge deklarieren wohl auch eher alles public als sich ueberhaupt um Zugriffslevel zu scheren. --The C language combines all the power of assembly language with all the ease-of-use of assembly language. Dieser Beitrag wurde am 11.08.2010 um 18:33 von Bluthund bearbeitet. |
Profil || Suche |
004 11.08.2010, 22:23 Kriz |
Sei mir nicht böse, aber du brauchst mir keine Abhandlung über C++ Vererbung zu halten. Was ich gesagt habe, ist einfach nur jahrelange Praxiserfahrung. In C++ dem Risiko eines Zugriffsmißbrauchs von Kindklassen nur durch konsequentes "privatisieren" der Elternklasse entgegenzutreten ist ein schlechter Ansatz, wenn es beispielsweise um Zugriffsgeschwindigkeit geht. Haben Kindklassen nur per geerbten (virtuellen) Methoden Zugriff auf Elternmembers, dann reduzieren sich die Geschwindigkeitsvorteile gegenüber einem Direktzugriff dramatisch. Ganz besonders sieht man sowas bei (selbstgestrickten) Templates, die mit fetten Matrizen, Vektoren oder Hashtables auf Hardcorelevel arbeiten. Wer hier auf deine Weise arbeitet, der verschenkt bare Kapazitäten und letztendlich teure Zeit. Wie gesagt... Einem Drittcoder/Kunden eine Lib anzudrehen, die ebenso auf deine Weise aufgebaut ist, macht dich mit Garantie nicht zu seinem Freund! Wer ableiten muß/darf von vererbungsfähigen Klassen, der will auch direkten Zugriff auf vererbungsrelevante Members und Methoden der Elternklasse haben. EDIT: Und niemand sollte sich in C++ etwas auf "private" einbilden. Das Teil läßt sich in Nullkommanix per C aushebeln ;-) --K:R-I)Z++ Dieser Beitrag wurde am 11.08.2010 um 22:25 von Kriz bearbeitet. |
Profil || Suche |
005 12.08.2010, 01:42 Bluthund |
Dafuer bietet C++ Inlining fuer hinreichend simple Funktionen und ausserdem hat man erst ein Performanceproblem wenn man es auch messen kann. Ich wage zu bezweifeln, dass es einen Menschen auf diesem Planeten gibt, der mit den Optimierungen, die ein guter C++-Compiler an den Tag legt, vom ersten Moment der Implementierung sagen kann wie schnell oder langsam sein Programm am Ende des Tages laufen wird und Leute, die dann trotzdem von Tag Eins an meinen saemtliche Ecken ihres Programms auf Performance zu trimmen, wandeln auf dem dunklen Pfad der Premature Optimization. Voellig korrekt, ein Zugriff durch die vtable kostet (auch wenn diese Kosten wahrscheinlich nur in performanzkritischen Applikationen ueberhaupt eine Rolle spielen und ansonsten vom praktischen Nutzen aufgewogen werden). Jetzt wuerde ich aber auch gern mal wissen welche Umstaende ueberhaupt einen virtuellen Mutator als Zugangspunkt fuer eine Membervariable der Basisklasse semantisch sinnvoll machen sollen? (Die Frage haette ich wirklich gern beantwortet) Soll er ja auch haben, aber eben nur ueber ein klar definiertes Interface (mit dem beide Seiten arbeiten koennen) und nicht mit einer Kernbohrung mitten durch saemtliche Klassen der Vererbungshierarchie. Denn spaetestens wenn du die Implementierung der Basisklasse veraenderst, wird bei letzterem Fall sogar ziemlich sicher der Kunde an dich herantreten und fragen warum er denn jetzt mit der teuer bezahlten neuen Version auch noch $x Mann-Stunden in die Anpassung seines Codes stecken muss. Es geht nicht darum mutwilligen sondern versehentlichen Missbrauch zu vermeiden. Denn Menschen machen Fehler und Software-Systeme werden auch eher komplexer als einfacher. Wenn ich ein "Betreten verboten"-Schild am Zaun aufstelle und trotzdem jemand ueber den Zaun steigt, dann darf sich dieser Mensch imho auch nicht wundern wenn er auf den Hund des Hauses trifft und das Grundstueck mit einem Koerperteil weniger verlaesst (oder ins Mun-Verbrauchschiessen der oertlichen Kaserne laeuft -- ich konnte mich nicht entscheiden welche Metapher den Punkt besser rueber bringt). Denn die Forcierung von Zugriffsleveln oder auch const-Correctness durch den Compiler sind eben dafuer da dem Programmierer zu helfen (wenn sie denn mit Sinn und Verstand eingesetzt werden) und nicht um ihn zu gaengeln. Und wer meint diese Hilfen umgehen zu muessen und damit u.U. Code ausserhalb seiner Parameter betreibt, der braucht sich auch nicht wundern wenn bei dessen Ausfuehrung dann ploetzlich sein Haus in Flammen aufgeht oder die automatisierte Boersensoftware ploetzlich nur noch wie wild kauft statt zu verkaufen. Ich verstehe vollkommen, dass du hier aus deiner eigenen Erfahrung sprichst und dementsprechende Empfehlungen machst. Aber Fakt ist auch, dass die Realitaet der Software-Entwicklung besonders in so maechtigen Sprachen wie C++ viele Dinge zugelassen hat, die schlicht und ergreifend fuer den Allerwertesten sind. Ich hatte bereits das "Vergnuegen" Maintenance an einer mit "Just get it done"-Mentalitaet entstandenen OO-Architektur zu leisten und ich kann dir sagen, dass ich soweit moeglich meinen Nachfolgern nicht so etwas hinterlassen moechte. Abschliessend noch: Jo, Zeit ist Geld. Aber Zeit hat auch einen relativen Wert. Denn Mann-Stunden sind zumindest in der westlichen Welt teurer als Hardware. Und die Vergangenheit hat gezeigt, dass Software immer laenger lebt als man denkt und viel Zeit und damit Geld fuer deren Wartung ausgegeben wird. Zeit von der ein Teil von vielen dieser hellen Koepfe ganz anderen Problemen haette gewidmet werden koennen wenn man von Anfang an etwas mehr Acht gegeben haette. -- The C language combines all the power of assembly language with all the ease-of-use of assembly language. |
Profil || Suche |
006 12.08.2010, 13:34 Kriz |
Du hättest Philosoph werden sollen :) Ansonsten lese ich viel Hysterie aus deiner Antwort raus, von bösen Absichten, veralteten Konzepten und schwachen Dogmata. Kann ich alles nicht so bestätigen, was aber nicht heißen soll, daß es auch Firmen und Leute gibt, die genauso nach den Vorstellungen handeln und arbeiten, die du hier aufgestellt hast. Aber darüber zu diskutieren bringt nichts, denn daß ist wie immer eine Geschmackssache. Die präventive Vollkapselung gegenüber Kindklassen ohne sinnvollen Einsatz von protected Elementen ist meiner Meinung und Erfahrung nach vollkommener Schwachsinn. Wie gesagt: Geschmackssache, denn keines deiner Argumente gegen diese Technik hat mich überzeugt. Nicht mal ansatzweise. Drum lassen wir es gutsein, sonst wird das eh wieder nur ein fruchtloser Diskussionsthread. Merci :) --K:R-I)Z++ |
Profil || Suche |
007 12.08.2010, 13:54 ElChupkapres |
Erst mal vielen Dank ihr beiden. Jetzt da man es weiß macht es natürlich Sinn! Ich hab den Kopierkonstruktor und den operator=() überschrieben und es funktioniert. Trotzdem würde ich euch bitten noch mal schnell einen Blick drauf zu werfen:
1. Ist das delete[] m_array beim Überladen des Operators notwendig? 2. m_size = copyThis.m_size; Ist das hier ein Sonderfall das ich einfach auf m_size von copyThis zugreifen kann? Normalerweise sollte doch der Zugriff nicht möglich sein weil es private ist oder irre ich mich da? 3. Gibt es eine effizientere Möglichkeit das Array zu kopieren evtl. mit memcpy? Bleibt noch zu sagen: @private/protected: Bei Klasse C war es ein Schreibfehler, sollte private sein. Bei Klasse B ist es protected weil B Vaterklasse ist. Btw: Interessante Diskussion die ihr führt :) @Iteratoren: Ja das wollte ich mir eigentlich schon lange näher angeguckt haben. Aber irgendwie hab ich bis jetzt den Vorteil noch nicht so richtig erkannt und in diesem Fall ist es mit einer for-Schleife so schön einfach... -- |
Profil || Suche |
008 12.08.2010, 14:39 Bluthund |
1. Ja, sonst leakst du den Speicher von m_array da du 2 Zeilen spaeter ja den Pointer per new ueberschreibst. 2. Du befindest dich innerhalb der selben Klasse womit du vollen Zugriff auf jede der von ihr deklarierten Member hast. public, protected und private wirken nur auf Zugriffe von ausserhalb der Klasse. 3. Du kannst es mit memcpy versuchen. Das kann unter Umstaenden effizienter sein, wenn deine Implementierung groessere (aber trotzdem sinnvoll dimensionierte) Bloecke kopiert als du es mit deiner for-Schleife tust. Aber wie schon oben gesagt kann man Laufzeit-Effizienz nur messen und nicht schaetzen. edit: The C language combines all the power of assembly language with all the ease-of-use of assembly language. Dieser Beitrag wurde am 12.08.2010 um 15:11 von Bluthund bearbeitet. |
Profil || Suche |
009 19.08.2010, 15:48 ElChupkapres |
Also noch mal vielen Dank, damit markiere ich das Thema mal als gelöst. -- |
Profil || Suche |