Come risolvere l'interdipendenza di classe nel mio codice C ++?

10

Nel mio progetto C ++, ho due classi, Particle e Contact . Nella classe Particle , ho una variabile membro std::vector<Contact> contacts che contiene tutti i contatti di un oggetto Particle e le funzioni membro corrispondenti getContacts() e addContact(Contact cont) . Pertanto, in "Particle.h", includo "Contact.h".

Nella classe Contact , vorrei aggiungere codice al costruttore per Contact che chiamerà Particle::addContact(Contact cont) , in modo che contacts sia aggiornato per entrambi gli oggetti Particle tra cui Contact oggetto è stato aggiunto. Quindi, dovrei includere "Particle.h" in "Contact.cpp".

La mia domanda è se questa sia accettabile o buona prassi di codifica e, in caso contrario, quale sarebbe un modo migliore per implementare ciò che sto cercando di ottenere (semplicemente inserire, aggiornando automaticamente l'elenco dei contatti per una particella specifica ogni volta viene creato un nuovo contatto).

Queste classi saranno legate insieme da una classe Network che avrà particelle N ( std::vector<Particle> particles ) e contatti Nc ( std::vector<Contact> contacts ). Ma volevo essere in grado di avere funzioni come particles[0].getContacts() - va bene avere tali funzioni nella classe Particle in questo caso, o c'è una migliore "struttura" di associazione in C ++ per questo scopo (di due classi correlate essere usato in un'altra classe).

Potrei avere bisogno di un cambiamento di prospettiva qui in come mi sto avvicinando a questo. Poiché le due classi sono connesse da un oggetto di classe Network , è tipico dell'organizzazione codice / classe avere informazioni sulla connettività interamente controllate dall'oggetto Network (nel senso che un oggetto Particle non dovrebbe essere a conoscenza dei suoi contatti e, di conseguenza, non dovrebbe avere una funzione membro getContacts() ). Quindi, per sapere quali contatti ha una particella specifica, avrei bisogno di ottenere quell'informazione attraverso l'oggetto Network (ad es. Usando network.getContacts(Particle particle) ).

Sarebbe meno tipico (forse anche scoraggiato) il design della classe C ++ per un oggetto Particle per avere anche quella conoscenza (cioè, avere più modi di accedere a tali informazioni - attraverso l'oggetto Network o l'oggetto Particle, qualunque sia sembra più conveniente)?

    
posta AnInquiringMind 08.11.2017 - 07:53
fonte

5 risposte

18

Ci sono due parti nella tua domanda.

La prima parte è l'organizzazione dei file di intestazione e dei file sorgente di C ++. Questo viene risolto usando la forward statement e la separazione della dichiarazione della classe (inserendoli nel file di intestazione) e del body del metodo (inserendoli nel file sorgente). Inoltre, in alcuni casi si può applicare l' idioma Pimpl ("puntatore all'implementazione") per risolvere casi più difficili . Utilizza i puntatori della proprietà condivisa ( shared_ptr ), i puntatori a proprietà singola ( unique_ptr ) e i puntatori non proprietari (puntatore raw, ovvero "asterisco") in base alle best practice.

La seconda parte è come modellare oggetti che sono correlati tra loro sotto forma di un grafico . I grafici generali che sono not DAG (grafici aciclici diretti) non hanno un modo naturale di esprimere la proprietà di tipo ad albero. Invece, i nodi e le connessioni sono tutti i metadati che appartengono a un singolo oggetto grafico. In questo caso, non è possibile modellare la relazione nodo-connessione come aggregazioni. I nodi non "possiedono" connessioni; le connessioni non "possiedono" i nodi. Invece, sono associazioni, e sia i nodi che le connessioni sono "di proprietà" del grafico. Il grafico fornisce i metodi di interrogazione e manipolazione che operano sui nodi e sulle connessioni.

    
risposta data 08.11.2017 - 09:32
fonte
5

Se ti ho capito bene, lo stesso oggetto di contatto appartiene a più di un oggetto particella, poiché rappresenta una sorta di contatto fisico tra due o più particelle, giusto?

Quindi la prima cosa che penso sia discutibile è perché Particle ha una variabile membro std::vector<Contact> ? Dovrebbe invece essere std::vector<Contact*> o std::vector<std::shared_ptr<Contact> > . addContact quindi dovrebbe avere una firma diversa come addContact(Contact *cont) o addContact(std::shared_ptr<Contact> cont) invece.

Ciò rende superfluo includere "Contact.h" in "Particle.h", una dichiarazione in avanti di class Contact in "Particle.h" e una inclusione di "Contact.h" in "Particle.cpp" sarà sufficiente.

Quindi la domanda sul costruttore. Vuoi qualcosa come

 Contact(Particle &p1, Particle &p2)
 {
      p1.addContact(this);
      p2.addContact(this);
 }

A destra? Questo design è ok, purché il tuo programma sappia sempre le particelle correlate nel momento in cui un oggetto contatto deve essere creato.

Nota, se passi alla rotta std::vector<Contact*> , devi investire alcuni pensieri sulla durata e sulla proprietà degli oggetti Contact . Nessuna particella "possiede" i suoi contatti, probabilmente un contatto dovrà essere eliminato solo se vengono distrutti entrambi gli oggetti Particle correlati. L'utilizzo di std::shared_ptr<Contact> risolverà automaticamente questo problema. Oppure lasci che un oggetto "contesto circostante" diventi proprietario di particelle e contatti (come suggerito da @rwong) e ne gestisca la durata.

    
risposta data 08.11.2017 - 09:40
fonte
0

Sì, ciò che descrivi è un modo molto accettabile per garantire che ogni istanza di Contact sia presente nell'elenco di contatti di Particle .

    
risposta data 08.11.2017 - 08:27
fonte
0

Quello che hai fatto è corretto.

Un altro modo ... Se l'obiettivo è quello di garantire che ogni Contact sia in una lista, allora potresti:

  • creazione di blocchi di Contact (costruttori privati),
  • inoltra dichiara Particle class,
  • crea Particle class un amico di Contact ,
  • in Particle crea un metodo factory che crea un Contact

Quindi non devi includere particle.h in contact

    
risposta data 08.11.2017 - 09:31
fonte
0

Un'altra opzione che potresti prendere in considerazione è creare un costruttore di contatti che accetti un riferimento di particelle. Ciò consentirà a un contatto di aggiungersi a qualsiasi contenitore che implementa addContact(Contact) .

template<class Container>
Contact(/*parameters*/, Container& container)
{
  container.addContact(*this);
}
    
risposta data 08.11.2017 - 18:44
fonte

Leggi altre domande sui tag