destruktor von einer eigenen klasse aufrufen?
-
hallo jungs und natürlich mädels...
ich bin neu bei c++ und komme eigentlich aus der java welt und habe da eine kleine frage zum destruktor von einer klasse und wann man diesen aufruft und auch noch eine wie man eigentlich in c++ mit klassen arbeitet...
also ich versuch das mal so zu formulieren,
in c++ kann ich ja einen pointer auf eine klasse haben, eine referenz oder eben die klassen variable selber (ich weiß gar nicht ob das auch eine bezeichnung hat)meine frage ist wenn ich jetzt eine klasse erstelle habe ich ja die qual der wahl, für was entscheide ich mich dann eigentlich?
für eine normale variable, einen pointer oder eine referenz?
(ich weiß das referenzen gerne nur bei funktionen/mehtoden gerne genutzt werden bzw genutzt werden sollten)ich habe mal ein kleines beispiel programm geschrieben um mein "problem" zu schildern...
hier mal mit der "pointer" variante:
#include <iostream> using namespace std; class MyString { char* s; int len; public: MyString(char* s); ~MyString(); void print(); }; MyString::MyString(char* s) { len = strlen(s); this->s = new char[len + 1]; strcpy(this->s, s); } MyString::~MyString() { delete [] s; cout << "~MyString()" << endl; } void MyString::print() { int i = 0; while (*(s + i) != '\0') { cout << *(s + i); i++; } cout << endl; } int main(int argc, const char * argv[]) { MyString *ms = new MyString("Das ist ein Test"); ms->print(); delete ms; cout << "fin" << endl; return 0; }
soweit funktioniert alles, ich kann auch mit delete gezielt die klasse löschen und den speicher frei geben, also ich kann selber den destruktor auslösen...
das einzige komisch eist eben das ich mit dem -> operator arbeiten muss wenn ich methoden aufrufen möchte...hier jetzt mal die variante mit einer "normalen variable":
#include <iostream> using namespace std; class MyString { char* s; int len; public: MyString(char* s); ~MyString(); void print(); }; MyString::MyString(char* s) { len = strlen(s); this->s = new char[len + 1]; strcpy(this->s, s); } MyString::~MyString() { delete [] s; cout << "~MyString()" << endl; } void MyString::print() { int i = 0; while (*(s + i) != '\0') { cout << *(s + i); i++; } cout << endl; } int main(int argc, const char * argv[]) { MyString ms("Das ist ein Test"); ms.print(); //delete ms; <- geht nicht!? cout << "fin" << endl; return 0; }
wie man sieht kann ich bei dieser variante mit dem . operator arbeiten (was ich auch aus java gewohnt bin) aber wenn ich so arbeite kann ich den destruktor nicht gezielt aufrufen, ich muss warten bis das ende der jeweiligen funktion erreicht wird und diese dann den destruktor aufruft...
die dritte variante mit referenzen scheint wohl gar nicht nicht zu gehen,
also man kann wohl nicht direkt eine referenz erstellen...
also sowas in der art:MyString& ms = new MyString("foo");
deswegen meine frage jetzt, wie arbeitete man eigentlich in c++ mit klassen?
immer mit pointern? immer als normale variable? oder kommt es immer drauf an?
wie kann ich bei nicht pointern den destruktor direkt aufrufen, geht das überhaupt?thx @ all!
-
Eigentlich möglichst immer mit "normalen Variablen". Du kannst die Lebenszeit der Variable auch kleine machen, z.B so:
int main() { { MyString m("Dies ist ein Test"); m.test(); } // Hier wird der String zerstört (Destruktor wird aufgerufen) std::cout << "fin" << std::endl; }
Mit rohen Pointern muss man in C++ so gut wie nie arbeiten, in C++11 oder in Boost gibt es dafür "Smartpointer", das sind Klassen, die Pointer wrappen und automatisch freigeben wenn der Smartpointer zerstört wird.
-
nullpointerexception schrieb:
meine frage ist wenn ich jetzt eine klasse erstelle habe ich ja die qual der wahl, für was entscheide ich mich dann eigentlich?
für eine normale variable, einen pointer oder eine referenz?Wenn du etwas lokal verwendest, ist eine normale Variable immer der normale weg. Zeiger werden bei sauberer Programmierung in der Regel wenn gekapselt (z.B. über die STL-Container, Smartpointer...), und du kannst NIEMALS ein Objekt als Referenz anlegen, da Referenzen nur ein Aliasname eines Objektes sind (Sprich: auf etwas anderes Verweisen).
Java-Referenzen haben mit C++ Referenzen nichts zu tun, und entsprechen intern am ehesten den C++ Zeigern, mit einer Referenzsyntax. C++ Referenzen wiederum müssen noch nicht einmal eine direkte Entsprechung im Maschinencode haben, da sie wie gesagt nur ein anderer Name für einen anderen Speicherbereich sind (Je nach Optimierungsfähigkeiten und Situation können sie daher vom Compiler wegoptimiert, oder in einen Zeiger umgewandelt werden - aber das ist interne "Compilermagie").
nullpointerexception schrieb:
soweit funktioniert alles, ich kann auch mit delete gezielt die klasse löschen und den speicher frei geben, also ich kann selber den destruktor auslösen...
An deinen Code sind ein paar Dinge nicht in Ordnung, unter anderem solltest du mal nach der "Regel der Großen Drei" suchen. Sobald man dynamische Speicherallokation in einer Klasse verwendet ist diese sehr wichtig.
nullpointerexception schrieb:
das einzige komisch eist eben das ich mit dem -> operator arbeiten muss wenn ich methoden aufrufen möchte...
Ein Zeiger ist nunmal kein Objekt, sondern eine Variable mit einer Speicheradresse. Über "->" erfolgt die Umsetzung von der Speicheradresse auf das Objekt.
nullpointerexception schrieb:
hier jetzt mal die variante mit einer "normalen variable"..
wie man sieht kann ich bei dieser variante mit dem . operator arbeiten (was ich auch aus java gewohnt bin) aber wenn ich so arbeite kann ich den destruktor nicht gezielt aufrufen, ich muss warten bis das ende der jeweiligen funktion erreicht wird und diese dann den destruktor aufruft...
Das ist auch bei Stackvariablen so normal, und wird bei Java soviel ich weiß bei beispielsweise "int" auch gemacht.
nullpointerexception schrieb:
die dritte variante mit referenzen scheint wohl gar nicht nicht zu gehen,
also man kann wohl nicht direkt eine referenz erstellen...
also sowas in der art:MyString& ms = new MyString("foo");
Logisch. ms ist ein Aliasname auf eine Objektvariable. Erstens existiert hier keine Variable, und zweitens weist du einen Zeiger und kein Objekt zu.
MyString ms; MyString & msref = ms; // msref ist nur ein anderer Name für ms MyString * ms2 = new MyString("foo"); MyString & ms2ref = *ms2; // msref ist nur ein anderer Name für das Objekt hinter ms2
[/quote]
nullpointerexception schrieb:
deswegen meine frage jetzt, wie arbeitete man eigentlich in c++ mit klassen?
immer mit pointern? immer als normale variable? oder kommt es immer drauf an?
wie kann ich bei nicht pointern den destruktor direkt aufrufen, geht das überhaupt?1. Konstruktoren ruft man in der Regel in 99,99% niemals direkt auf (auch delete ruft diesen nur indirekt auf, direkt wäre über den Methodenname z.B. "ms.~MyString()").
2. Nach Möglichkeit verwendet man normale Objekte (u.a. weil man dann die Freigabe automatisch am Ende des Scopes - Nicht nötigerweise der Funktion - kostenfrei bekommt, und zudem keinen Overhead [Adresse, indirekte Aufrufe...] hat). In manchen Fällen sind aber Zeiger die bessere Wahl, wobei man hier eher auf STL-Container oder Smartpointer zurück greift.
-
das bedeutet ich muss dann auch nicht wirklich delete aufrufen um eine klasse zu löschen?!
mir ist gerade was in den kopf gekommen...
was passiert wenn ich eine klasse in der klasse habe?also angenommen ich habe klasse auto und diese klasse auto hat als member die klasse motor...
muss ich dann im desturktor von auto irgendwie expliziet angeben den destruktor von motor aufzurufen oder passiert das automatisch?
(sollte es ja eigentlisch schließlich kann ich ja kein delete machen hehe...)
-
nullpointerexception schrieb:
das bedeutet ich muss dann auch nicht wirklich delete aufrufen um eine klasse zu löschen?!
Wenn du sie mit new allokierst, musst du sie auch mit delete freigeben. Den Destrukutor wiederum wirst du vermutlich niemals händisch aufrufen (delete ist kein direkter Destruktoraufruf, auch wenn delete diesen indirekt aufruft).
nullpointerexception schrieb:
also angenommen ich habe klasse auto und diese klasse auto hat als member die klasse motor...
muss ich dann im desturktor von auto irgendwie expliziet angeben den destruktor von motor aufzurufen oder passiert das automatisch?
Bei Membervariablen (Sofern du diese nicht dynamisch allozierst) wird die Freigabe ebenso am Ende des Scopes erfolgen, der Scope ist hierbei die Lebensdauer des Objektes das diese enthält.