Dubbelriktad har-relation, C++

Permalänk
Medlem

Dubbelriktad har-relation, C++

Hur implementerar man en dubbelriktad har-relation i C++?

Ex.

fil: klass1.h

/* begin: klass1.h */ #include "klass2.h" class klass1{ private: klass2 var; } /* end: klass1.h */

fil: klass2.h

/* begin: klass2.h */ #include "klass1.h" class klass2{ private: klass1 var; } /* end: klass2.h */

Hur ska man ändra denna kod så att den går igenom kompilatorn?

Visa signatur

"...trying is the first step towards failure." - Homer Simpson

Permalänk
Medlem

Igen..

class klass1; class klass2; class klass1 { private: klass2 *var; }; class klass2 { private: klass1 *var; };

Skulle jag göra, finns säkert andra sätt.

Visa signatur

Perl - Made by Idiots, Java - Made for Idiots, C++ - Envied by Idiots

Permalänk
Medlem

Problemet löste sig på ett annat sätt.

Fotnot: Problemet var ju lite förenklat i inlägget.

Visa signatur

"...trying is the first step towards failure." - Homer Simpson

Permalänk
Medlem

i den översta kodensnutten, blir det inte en oändlig rekursion där?

Visa signatur

hmm...

Permalänk
Medlem

Jopp...

Visa signatur

Perl - Made by Idiots, Java - Made for Idiots, C++ - Envied by Idiots

Permalänk

Jag vet inte hur du löste problemet, men när två klasser behöver direkt prata med varandra är man oftast ute på hal is. Ett vanligt sätt, design pattern, för att komma undan detta är att införa ytterligare en klass emellan de två klasserna som sköter kommunikationen åt ena hållet.

Ta exemplet att man vill göra en ftp-klient och lägger själva protokoll-hanteringen i en klass CFtpClient och användargränssnittet i en klass CApplicationWindow.

CApplicationWindow äger typiskt en CFtpClient, och kan anropa dess metoder (som exempelvis Connect, GetFile och PutFile).

Samtidigt kanske man i användargränssnittet vill visa en procent-stapel på hur mycket som hämtats av en fil, eller status under en Connect. Den första tanken är att CFtpClient ska anropa tillbaka till CApplicationWindow (med metoder som exempelvis SetStatus eller GotDisconnected).

Problemet blir då ett obehagligt cirkulärberoende. Istället inför man en klass "CStatusReceiver", som är abstrakt - en så kallad "protokollklass". Dess enda uppgift är att tala om på vilket sätt CFtpClient ska meddela CApplicationWindow vad som händer. CApplicationWindow ärver, eller "implementerar" protokollklassen CStatusReceiver.

Ungefär såhär skulle det kunna se ut:

// Abstrakt klass, används enkom för att skicka data // mellan användar-gränssnittet och ftp-hanteraren. // Den här skulle typiskt ha fler metoder, typ // OnConnected(), OnTransferCompleted, OnConnectionLost etc. class CStatusReceiver { public: virtual void OnStatus(const char* statusText) = 0; }; // Själva protokoll-hanteringen. Den här skulle typiskt ha fler // metoder, typ Connect(), GetFile(), PutFile(). class CFtpClient { public: CStatusReceiver* m_pStatusReceiver; CFtpClient(CStatusReceiver* pStatusReceiver) : m_pStatusReceiver(pStatusReceiver) { } // Anropas för att koppla upp mot en server. Skickar // statusmedddelanden till CStatusReceiver för att // indikera vad som händer. void Connect(const char* ipAddress, unsigned port) { // m_pStatusReceiver->OnStatus("Connecting to..."); } }; class CApplicationWindow : public CStatusReceiver { CFtpClient* m_pFtp; public: CApplicationWindow() : m_pFtp(NULL) { m_pFtp = new CFtpClient(this); } virtual ~CApplicationWindow() { delete m_pFtp; } // CStatusReceivers metod som anropas av CFtpClient-medlemmen då den vill meddela ny status void OnStatus(const char* statusText) { // Uppdatera statustext, exempelvis m_LogWindow->AddText(statusText); } };

Med denna lösning vet både CFtpClient och CApplicationWindow om CStatusReceiver.

CApplicationWindow känner dessutom till CFtpClient.

Inga cirkulära beroenden finns.

Permalänk
Medlem

Jag lärde mig igår att man kan göra på följande sätt:

klass1.h

/* klass1.h */ class klass2; // kallas klassdeklaration class klass1{ public: void set(klass2); private: klass2 var; };

klass1.cpp

/* klass1.cpp */ #include "klass2.h" void klass1::set(klass2 v){ var = v; }

Sedan gör man samma sak för klass2. Då går det igenom kompilatorn.

Visa signatur

"...trying is the first step towards failure." - Homer Simpson

Permalänk
Medlem

Oavsett om det går att lösa så är det ingen bra lösning att två klasser pratar direkt mot varandra. Ansvarsbiten blir ofta problematisk:
* Vem ansvarar för att ta bort den andra?
* Vad händer om den ena skickar till den andra när den i själva verket är borttagen?
* Kan det uppstå deadlock?
...

Försök undvika denna form i möjligaste mån, developer visade en variant.

Visa signatur

/ Assar

Permalänk
Medlem

Är väll bätte att ha en var i private: i klassen och anropa en genom en funktion?

Visa signatur

"Obay your leader"