W moim projekcie C ++ mam dwie klasy Particle
i Contact
. W Particle
klasie mam zmienną składową, std::vector<Contact> contacts
która zawiera wszystkie kontakty Particle
obiektu oraz odpowiednie funkcje składowe getContacts()
i addContact(Contact cont)
. Zatem w „Particle.h” dołączam „Contact.h”.
W Contact
klasie chciałbym dodać kod do konstruktora, aby Contact
ten wywołał Particle::addContact(Contact cont)
, aby contacts
był aktualizowany zarówno dla Particle
obiektów, między którymi Contact
obiekt jest dodawany. Dlatego musiałbym dołączyć „Particle.h” do „Contact.cpp”.
Moje pytanie brzmi, czy jest to akceptowalna / dobra praktyka kodowania, a jeśli nie, jaki byłby lepszy sposób na wdrożenie tego, co próbuję osiągnąć (po prostu, automatycznie aktualizując listę kontaktów dla określonej cząstki za każdym razem, gdy nowy kontakt jest tworzone).
Klasy te zostaną połączone razem Network
klasą, która będzie miała cząsteczki N ( std::vector<Particle> particles
) i styki Nc ( std::vector<Contact> contacts
). Ale chciałem particles[0].getContacts()
mieć takie funkcje - czy Particle
w tym przypadku można mieć takie funkcje w klasie, czy też istnieje lepsza „struktura” asocjacji w C ++ do tego celu (z dwóch powiązanych klas używanych w innej klasie) .
Może potrzebuję zmiany perspektywy w tym, w jaki sposób do tego podchodzę. Ponieważ obie klasy są połączone przez Network
obiekt klasy, czy typową organizacją kodu / klasy jest posiadanie przez Network
obiekt pełnej kontroli informacji o łączności (w tym sensie, że obiekt cząstek nie powinien być świadomy swoich kontaktów, a zatem nie powinien mieć getContacts()
członka funkcjonować). Następnie, aby wiedzieć, jakie kontakty ma konkretna cząstka, musiałbym uzyskać tę informację przez Network
obiekt (np. Za pomocą network.getContacts(Particle particle)
).
Czy mniej typowy (być może nawet zniechęcony) projekt klasy C ++ dla obiektu Particle miałby również tę wiedzę (tj. Miałby wiele sposobów dostępu do tych informacji - za pośrednictwem obiektu Network lub obiektu Particle, w zależności od tego, co wydaje się wygodniejsze )?
źródło
Network
obiekt klasy, który zawieraParticle
obiekty iContact
obiekty. Mając tę podstawową wiedzę, mogę następnie spróbować ocenić, czy pasuje ona do moich konkretnych potrzeb, które wciąż są badane / rozwijane w miarę rozwoju projektu.Odpowiedzi:
Twoje pytanie składa się z dwóch części.
Pierwsza część to organizacja plików nagłówkowych C ++ i plików źródłowych. Aby rozwiązać ten problem, należy użyć deklaracji forward i oddzielić deklarację klasy (umieszczając je w pliku nagłówkowym) i treść metody (umieszczając je w pliku źródłowym). Ponadto w niektórych przypadkach można zastosować idiom Pimpl („wskaźnik do implementacji”), aby rozwiązać trudniejsze przypadki. Używaj wskaźników współwłasności (
shared_ptr
), wskaźników pojedynczego prawa własności (unique_ptr
) i wskaźników niebędących właścicielami (wskaźnik nieprzetworzony, tj. „Gwiazdka”) zgodnie z najlepszymi praktykami.Druga część dotyczy modelowania obiektów powiązanych ze sobą w formie wykresu . Ogólne wykresy, które nie są DAG (ukierunkowane wykresy acykliczne), nie mają naturalnego sposobu wyrażania własności drzewiastych. Zamiast tego wszystkie węzły i połączenia są metadanymi należącymi do jednego obiektu wykresu. W takim przypadku nie można modelować relacji połączenie-węzeł jako agregacji. Węzły nie „posiadają” połączeń; połączenia nie „posiadają” węzłów. Zamiast tego są to skojarzenia, a zarówno węzły, jak i połączenia są „własnością” wykresu. Wykres przedstawia metody zapytań i manipulacji, które działają na węzłach i połączeniach.
źródło
particles[0].getContacts()
- czy sugerujesz w swoim ostatnim akapicie, że nie powinienem mieć takich funkcji wParticle
klasie, czy też obecna struktura jest w porządku, ponieważ są one ze sobą powiązane / powiązaneNetwork
? Czy w tym przypadku jest lepsza „struktura” asocjacji w C ++?network.particle[p]
będzie odpowiadaćnetwork.contacts[p]
wskaźnikom jej kontaktów. W przeciwnym razie sieć i cząsteczki w jakiś sposób śledzą te same informacje.Particle
obiekt nie powinien być świadomy swoich kontaktów (więc nie powinienem miećgetContacts()
funkcji członka) i że ta informacja powinna pochodzić tylko zNetwork
obiektu? Czy źle byłoby zaprojektować klasę C ++ dlaParticle
obiektu, który miałby tę wiedzę (tj. Miałby wiele sposobów dostępu do tej informacji - za pośrednictwemNetwork
obiektu lubParticle
obiektu, w zależności od tego, co wydaje się wygodniejsze)? To drugie wydaje mi się bardziej sensowne, ale może muszę zmienić moje zdanie na ten temat.Particle
znajomością czegokolwiek na tematContact
s lubNetwork
s polega na tym, że wiąże cię to z określonym sposobem reprezentowania tego związku. Wszystkie trzy klasy mogą musieć się zgodzić. Jeśli zamiast tegoNetwork
jest jedynym, który wie lub dba o to, to tylko jedna klasa musi się zmienić, jeśli uznasz, że inna reprezentacja jest lepsza.Particle
iContact
powinien być całkowicie oddzielny, a powiązanie między nimi jest zdefiniowane przezNetwork
obiekt. Aby być całkowicie pewnym, to (prawdopodobnie) miał na myśli @rwong, kiedy napisał: „zarówno węzły, jak i połączenia są„ własnością ”wykresu. Wykres zapewnia metody zapytań i manipulacji, które działają na węzłach i połączeniach”. , dobrze?Jeśli dobrze zrozumiałem, ten sam obiekt kontaktowy należy do więcej niż jednego obiektu cząsteczkowego, ponieważ reprezentuje pewien rodzaj fizycznego kontaktu między dwiema lub więcej cząsteczkami, prawda?
Pierwszą rzeczą, która moim zdaniem jest wątpliwa, jest to, dlaczego
Particle
zmienna członkowska jest zmiennastd::vector<Contact>
? Powinien to być astd::vector<Contact*>
lubstd::vector<std::shared_ptr<Contact> >
zamiast.addContact
powinien mieć inny podpis, jakaddContact(Contact *cont)
lubaddContact(std::shared_ptr<Contact> cont)
zamiast.To sprawia, że dołączanie „Contact.h” w „Particle.h” nie jest konieczne, wystarczy deklaracja
class Contact
„w. Particle.h” i dołączenie „Contact.h” w „Particle.cpp”.Następnie pytanie o konstruktora. Chcesz coś takiego
Dobrze? Ten projekt jest w porządku, o ile twój program zawsze zna powiązane cząstki w momencie, w którym trzeba utworzyć obiekt kontaktowy.
Uwaga: jeśli wybierzesz
std::vector<Contact*>
trasę, musisz zainwestować kilka przemyśleń na temat żywotności i własnościContact
obiektów. Żadna cząstka nie jest właścicielem swoich kontaktów, kontakt prawdopodobnie będzie musiał zostać usunięty tylko wtedy, gdy oba powiązaneParticle
obiekty zostaną zniszczone. Użyciestd::shared_ptr<Contact>
zamiast tego automatycznie rozwiąże ten problem. Albo pozwalasz obiektowi „otaczającemu kontekstowi” przejąć własność cząstek i kontaktów (jak sugeruje @rwong) i zarządzać ich żywotnością.źródło
addContact(const std::shared_ptr<Contact> &cont)
ponadaddContact(std::shared_ptr<Contact> cont)
?move
paradygmatuparticle1.getContacts()
iparticle2.getContacts()
dostarczenie tego samegoContact
obiektu reprezentującego fizyczny kontakt międzyparticle1
iparticle2
, a nie dwa różne obiekty. Oczywiście można spróbować zaprojektować system w taki sposób, że nie ma znaczenia, czyContact
w tym samym czasie dostępne są dwa obiekty reprezentujące ten sam kontakt fizyczny. Wymagałoby to uczynieniaContact
niezmiennego, ale czy jesteś pewien, że tego właśnie chcesz?Tak, to, co opisujesz, jest bardzo akceptowalnym sposobem upewnienia się, że każda
Contact
instancja znajduje się na liście kontaktówParticle
.źródło
To, co zrobiłeś, jest poprawne.
Inny sposób ... Jeśli celem jest upewnienie się, że każdy
Contact
znajduje się na liście, możesz:Contact
(konstruktorów prywatnych),Particle
klasę,Particle
klasę przyjacielemContact
,Particle
tworzeniu metody fabrycznej, która tworzyContact
Więc nie musisz się
particle.h
w to włączaćcontact
źródło
Network
klasy, czy to zmienia sugerowaną strukturę, czy nadal będzie takie samo?Inną opcją, którą możesz rozważyć, jest utworzenie konstruktora Contact, który akceptuje szablon Particle referencyjny. Umożliwi to kontaktowi dodanie się do dowolnego implementowanego kontenera
addContact(Contact)
.źródło