Objekt einer Klasse in Klasse verwenden
-
SeppJ schrieb:
@cooky451: Woher weißt du, dass er Komposition suchte, nicht Vererbung (in einer ihrer vielen Spielarten), Übergabe (in einer ihrer vielen Spielarten) oder sonstwas, das ich gerade vergessen habe (in einer seiner vielen Spielarten)?
Ich hab ihm einfach irgendwas geschrieben damit er einsieht, dass die Informationen nicht reichen. Geht meiner Erfahrung nach schneller als Diskussionen.
amolacar schrieb:
Genauer: Ich habe eine Klasse, die die socket-Funktionen der Win-API durch eigene Methoden ersetzt, dazu Fehlerbehandlung etc..
Das hat den Sinn, dass ich in der zweiten Klasse dann zum Beispiel nur noch createSocket() aufrufen muss wenn ich eins erstellen will.Such mal nach RAII.
-
RAII klingt auf jeden Fall interessant, wird hier meiner Meinung nach nicht benötigt.
Um nochmal zurück zu meiner Frage zu kommen:Wie kann ich diese "socket-Klasse" jetzt in meiner zweiten Klasse nutzen?
-
amolacar schrieb:
RAII klingt auf jeden Fall interessant, wird hier meiner Meinung nach nicht benötigt.
Nur auf die Art und Weise, wie ein Flugzeug nicht "nötig" ist, um von London nach New York zu kommen. Es mag auch ohne gehen, ist dann aber unnötig langsam und umständlich. Guck dir das unbedingt nochmal an. Das ist eine wichtige Grundtechnik!
Wie kann ich diese "socket-Klasse" jetzt in meiner zweiten Klasse nutzen?
Die Frage erübrigt sich, wenn die Klassen ihre Ressourcen selber verwalten, siehe oben.
-
wie meine voredner schon sagten: was hast du genau vor?
(Used sei im foglenden die zu nutzende Klasse, User die nutzende Klasse):
1.- du kannst User von Used erben lassen um die funktionalität zu nutzenclass Used { }; class User : public/private/protected Used {};2.- du kannst Used in User reinkomponieren
class Used { }; class User { Used m_used; };3.- du kannst dir innerhalb von methoden lokal ein Used objekt erstellen und dieses dann nutzen
class Used { }; class User { void foo() { Used u; } };das sind 3 grundlegende möglichkeiten, es geht noch mehr, aber rück erstmal infos raus

-
SeppJ schrieb:
@cooky451: Woher weißt du, dass er Komposition suchte, nicht Vererbung?
Aus dem Titel zu entnehmen.
-
Aber er kann auch eine noch schwächere Bindung gebrauchen:
class A { public: void foo(); }; class B { public: B() : a(nullptr) {} void SetA(A& a) {this->a = &a;} void func() {a->foo();} private: A* a; };oder sogar
class A { public: void foo(); }; class B { public: void someFunc(A& a) {a.foo();} };
-
Müsste es nicht so heißten:
[cpp]void SetA(A***** a) {this->a = a;} [/cpp]MfG
-
jain, er kann einen zeiger als parameter argument erwarten oder eine referenz, aber im falle der referenz (wie ers gemacht hat) muss er dann seiner membervaraible die addresse geben:
class A { public: void foo(); }; class B { public: B() : a(nullptr) {} void SetA(A& a) {this->a = &a;} // <---- void SetA(A * a) {this->a = a;}; void func() {a->foo();} private: A* a; };
-
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; }