Objekt einer Klasse in Klasse verwenden
-
Das war eigentlich eine Übung an den TE, aber da ihr es jetzt schon aufgelöst habt, hab ich es ebenfalls korrigiert.

yihaaa: Das ist eine semantische Frage. Ich finde die Referenzübergabe sinnvoller, weil ich meistens bei Dependency Injection nicht im Nachhinein einen Zeiger ungültig machen möchte. Soll man das nicht können, dann ist die Referenz zur Indikation dessen angebrachter, wie ich finde.
-
Ja die Übergabe per Referenz ist natürlich besser, da sie weniger Fehleranfällig ist als die mit Pointer. Wie schnell hat man mal das dereferenzieren vergessen...
Sorry, das wir deine Übung versaut haben
MfG
-
Erstmal vielen Dank für die tolle und kompetente Hilfe.
Ich habe mir das mit der RAII nochmal genauer angeguckt und wollte hier nochmal erläutern wie ich das verstehe:Wenn ich von meiner Socket-Klasse ausgehe erstelle ich mit jedem Objekt einen Socket, diesen kann ich dann mit Methoden verändern (an einen Port binden etc.) und durch den Destruktor in dem ich den Socket wieder freigebe wird automatisch, wenn ich z.B. ein Socket-Objekt in einer Methode in der Klasse in der ich die Socket-Klasse verwenden möchte erzeuge, dieses Objekt freigegeben, da mit dem verlassen der Funktion auch alle Variablen und Objekte zerstört werden?
Ist das so richtig?
-
Genau. Im Konstruktor erstellst du den Socket, und im Destruktor zerstörst du ihn wieder. Dann kannst du das nicht vergessen, und der Code ist Exception sicher. Wie genau man das jetzt modelliert könnte noch eine andere Frage sein, (connection/server/..) aber ich denke das bringt dir hier erstmal nicht genug Vorteile. Hauptsache RAII ist drin. Und vergiss den copy Konstruktor und den Zuweisungsoperator nicht.
(Wohl einfach private machen. Eventuell mal nach C++11 move suchen.)
-
Okay es hat sich ein weiteres Problemchen ergeben wo ich nochmal eure Hilfe brauche.
Als privat deklariert:
SOCKET _sockDas ist mein Konstruktor:
Socket::Socket() : _sock(socket(PF_INET, SOCK_STREAM, 0)) { if (_sock == INVALID_SOCKET) { throw socketException("socket() failed", WSAGetLastError()); } }Der Destruktor:
Socket::~Socket() { error = closesocket(_sock); if (error == SOCKET_ERROR) { throw socketException("closesocket() failed", WSAGetLastError()); } }Der Programmcode in dem der Fehler auftritt:
Socket saccept; Socket slisten; slisten.bind(SERVER_PORT, SERVER_ADDRESS); slisten.listen(); do { saccept.accept(slisten); } while (1);Nachdem die erste Verbindung aktzeptiert wurde und der erste Schleifendurchlauf beendet wurde bekomme ich eine Exception geworfen die mir sagt, dass closesocket() fehlgeschlagen ist mit dem Fehlercode 10038.
Kann mir jmd sagen was ich falsch mache?
-
da ich mich mit sockets nicht auskenne, kann ich nur eine schwere falle aufdecken.
NIEMALS IN DESTRUKTOREN EXCEPTIONS WERFEN !!!
ansonsten kann es passieren, dass objekte (oder arrays/vectoren) nicht komplett zerstört werden (oder im falle von arrays/listen einige nicht destruiert werden)
genaueres in effective c++ nachzulesen
-
Oha, gut zu wissen, danke.
Trotzdem frage ich mich, wieso nach dem ersten Schleifendurchlauf der Destruktor aufgerufen wird.Edit: Habe die Exception mal rausgenommen, jetzt wird nachdem die erste Verbindung aktzeptiert wurde gar keine Verbindung mehr angenommen, es wird aber auch keine Exception geworfen.
-
Zeig doch auch Mal accept.
-
void Socket::accept(Socket listen_sock) { _sock = ::accept(listen_sock._sock, NULL, NULL); }Der Fehler tritt aber denke ich bei dem listen-Socket auf, accept() bekommt nämlich kein gültiges Socket mehr übergeben. Nur wieso das so ist und wie man das beheben kann, keine Ahnung.
-
Hier sieht man ganz gut, wieso man die Regel der großen drei einhalten sollte. Wenn der Destruktor implementiert wird, sollten auch operator= und copy-ctor überschrieben werden. Wieso? Weil sonst Kopien erstellt werden, die natürlich auch durch RRID wieder zerstört werden. Und dann wird closesocket aufgerufen und alle noch lebenden Kopien der Klasse werden ungültig. Ein Nebeneffekt, der eigentlich fast nie so gewollt ist.
Was hier passiert:
accept wird mit listen_sock aufgerufen. Da Du nicht über Referenz übergibst, sondern by value, wird listen_sock erstellt, indem eine Kopie vom übergebenen Socket erstellt wird (Kopie heißt hier einfach, dass alle Attribute kopiert werden).Wieso listen_sock jetzt nicht gültig ist, weiß ich aber nicht. Ein Socket definiert sich ja meist intern über eine Handle-ID. Klar ist, dass nach dem Funktionsaufruf der Destruktor aufgerufen wird, sodass das Socket danach zerstört wird. Wenn Du by reference übergibst, wird eben keine Kopie erstellt. Wie macht sich denn bemerkbar, dass das Socket ungültig ist? Du kannst gerne auch Mal listen und bind zeigen.
-
amolacar schrieb:
void Socket::accept(Socket listen_sock) { _sock = ::accept(listen_sock._sock, NULL, NULL); }du übergibst eine kopie deines objektes (_sock) und diese kopie wird am ende von der methode zerstört. ich vermute, du hast in deiner socket klasse keine eigenen kopierkonstruktor definiert und der generierte kopiert stupide alles hin und her und sobald hier deine methode verlassen wird, werden quasi beide sockets ungültig gemacht, d.h. der eine wird zerstört und zerstört damit die aufgebaute verbindung des anderen
für mehr als dieses vermutungen müsste man sich code angucken
(versuch einfach mal:
void Socket::accept(Socket & listen_sock) { _sock = ::accept(listen_sock._sock, NULL, NULL); }nur mal so, ob der fehler dann weg ist. weil dann wäre meine vermutung bestätigt. das design solltest du dann mal überdenken)
-
Ja jetzt klappt alles, danke!

Also Kopier-Konstruktor sowas?
Socket::Socket(const Socket &rhs) { _sock = rhs._sock; }
-
Wenn du den so bauen willst, hast Du das Problem noch nicht verstanden.
Du kannst copy-ctor und operator= "deaktivieren", wenn man keine Kopien erstellen sollte. Das ist wohl in Deinem Fall zu empfehlen.
Einfach in die Klasse schreiben:
private: Socket(const Socket&); Socket& operator=(const Socket&);ohne Implementierung. Jetzt gehen keine Kopien mehr.