Domanda

Mi scuso se questo è stato chiesto prima, non sono abbastanza sicuro della terminologia o di come porre la domanda.

Mi chiedo se ci sono librerie o best practice per l'implementazione di modelli di oggetti in C ++. Se ho un insieme di classi in cui le istanze di queste classi possono avere relazioni reciproche e sono accessibili le une dalle altre tramite vari metodi, voglio scegliere un buon insieme di strutture dati sottostanti per gestire queste istanze e le loro relazioni. Questo è facile in Java poiché gestisce l'allocazione di memoria e la garbage collection per me, ma in C ++ devo farlo da solo.

Document Object Model (DOM) di HTML è un esempio; come altro esempio (inventato), supponiamo che io abbia queste classi:

  • Entity
  • Person (sottoclasse di Couple)
  • Property (sottoclasse di House)
  • Pet
  • Car (sottoclasse di home)
  • pets (sottoclasse di cars)
  • children (sottoclasse di spouse)

e queste relazioni:

  • marriage
    • ha 1 parents di classe members
    • ha 0 o più owner della classe std::map
    • ha 0 o più std::vector della classe <=>
    • ha 0 o più <=> della classe <=>
  • <=>
    • ha 0 o 1 <=> di classe <=>
    • ha 0 o 1 <=> di classe <=>
    • ha 0 o 1 <=> di classe <=> (in questo modello i genitori non esistono se non sono vivi!)
  • <=>
    • ha 2 <=> di classe <=>
  • <=>
    • ha 1 <=> di classe <=>

Ora che ho pensato a questi oggetti e alle loro relazioni, voglio iniziare a creare strutture di dati, metodi e campi per gestirli, ed ecco dove mi perdo, poiché devo occuparmi dell'allocazione della memoria e della gestione della vita e tutta quella roba. Puoi riscontrare problemi come i seguenti: Potrei voler mettere un oggetto in <=> o <=>, ma se lo faccio, non posso memorizzare puntatori a quegli oggetti poiché possono essere trasferiti quando la mappa o il vettore cresce o si restringe.

Un approccio che ho usato quando lavoravo molto con COM, è quello di avere una collezione nascosta che contenesse tutto. Ogni oggetto nella raccolta aveva un ID univoco (un numero o un nome), mediante il quale poteva essere cercato dalla raccolta e ogni oggetto aveva un puntatore alla raccolta. In questo modo, se hai un oggetto che vuole puntare a un altro oggetto, invece di tenere letteralmente un puntatore su un altro oggetto, memorizzo l'ID e posso cercarlo tramite la raccolta nascosta. Posso usare il conteggio dei riferimenti per gestire automaticamente i problemi della vita (tranne nel caso di cicli disgiunti, a volte non è un problema).

Ci sono altri approcci? O ci sono librerie per rendere questo tipo di cose più semplice in C ++?

modifica: quindi hai altri problemi, come le relazioni tra gli oggetti sono suscettibili di essere mutabili in molti casi e devi pensare in anticipo a come conservare i riferimenti agli oggetti e quali dovrebbero essere forniti metodi per accedere agli oggetti gli uni dagli altri. Ad esempio, se ho un handle per una <=> X e voglio rappresentare il concetto di & Quot; trova il figlio di X di nome George & Quot ;, quindi devo memorizzare il nome & Quot ; George quot &; piuttosto che un numero figlio: i bambini possono essere memorizzati in un vettore e potrei essere in grado di chiamare X.getChildCount () e X.getChild (0), ma " George " potrebbe non essere sempre il numero figlio 0, poiché altri bambini possono essere inseriti prima di " George " nel vettore figlio. Oppure X può avere altri due o tre o quattro figli anche chiamati & Quot; George & Quot ;. Oppure & Quot; George & Quot; può cambiare il suo nome in " Anthony " oppure " Georgina " ;. In tutti questi casi è probabilmente meglio usare una sorta di ID immutabile univoco.

modifica 2: (e pulirò un po 'la mia domanda una volta chiarita la questione)Sono in grado di gestire la scelta dei metodi e dei nomi delle proprietà, posso decidere se utilizzare una mappa o un elenco o un vettore. È abbastanza semplice. I problemi che sto cercando di affrontare in particolare sono:

  • come fare in modo che un oggetto memorizzi un riferimento a un altro oggetto, quando tali oggetti possono far parte di strutture dati riallocate
  • come gestire la gestione della durata degli oggetti, quando vi sono relazioni reciproche tra gli oggetti
È stato utile?

Soluzione

Hai scritto riguardo alla memorizzazione di oggetti dal tuo modello di oggetti all'interno di std :: vector ecc. e problemi con l'utilizzo di puntatori ad essi. Questo mi ricorda che è bene dividere le tue classi C ++ in due categorie (non sono sicuro della terminologia qui):

  1. Classi di entità che rappresentano oggetti che fanno parte del modello a oggetti. Di solito sono polimorfici o potenzialmente lo saranno in futuro. Sono creati su heap e sono sempre indicati da puntatori o puntatori intelligenti. Non li crei mai direttamente nello stack, come membri class / struct né li metti direttamente in contenitori come std :: vectors. Non hanno il costruttore di copie né operator = (puoi crearne una nuova con un metodo Clone). Puoi confrontarli (i loro stati) se è significativo per te ma non sono intercambiabili perché hanno identità. Ogni due oggetti sono distinti.

  2. Classi di valore che implementano tipi definiti dall'utente primitivi (come stringhe, numeri complessi, numeri grandi, puntatori intelligenti, handle wrapper, ecc.). Sono creati direttamente in pila o come membri class / struct. Sono copiabili con il costruttore della copia e operator =. Non sono polimorfici (polimorfismo e operatore = non funzionano bene insieme). Metti spesso le loro copie in contenitori stl. Raramente i puntatori vengono archiviati in posizioni indipendenti. Sono intercambiabili. Quando due istanze hanno lo stesso valore, puoi trattarle come uguali. (Le variabili che le contengono sono cose diverse però.)

Ci sono molte ottime ragioni per infrangere le regole. Ma ho osservato che ignorarli fin dall'inizio porta a programmi illeggibili, inaffidabili (soprattutto quando si tratta di gestione della memoria) e difficili da mantenere.


Ora torna alla tua domanda.

Se si desidera archiviare un modello di dati con relazioni complesse e un modo semplice per eseguire query come " trovare il figlio di X di nome George " perché non considerare un database relazionale in memoria?

Si noti che quando si intende implementare in modo efficiente a) relazioni bidirezionali più complesse eb) query basate su diverse proprietà degli oggetti, sarà probabilmente necessario creare strutture di dati indicizzate molto simili a quelle del database relazionale all'interno. Le tue implementazioni (come ce ne saranno molte, in un singolo progetto) saranno davvero più efficaci e robuste?

Lo stesso vale per " raccolte di tutto " e ID oggetto. Dovrai comunque tenere traccia delle relazioni tra gli oggetti per evitare id senza oggetti. In che cosa differisce dai puntatori? Altri poi ottengono errori significativi invece di impazzire in tutta la memoria, cioè ;-)


Alcune idee per la gestione della memoria:

  • Proprietà forte: quando puoi dichiarare che qualche entità vive solo finché il suo proprietario e non esiste alcuna possibilità di puntatori esistenti indipendentemente da essa, puoi semplicemente eliminarla nel distruttore del proprietario (o con scoped_ptr).

  • Qualcuno ha già proposto smart_ptr. Sono fantastici e possono essere utilizzati con contenitori stl. Tuttavia sono basati su computer di riferimento, quindi non creare cicli :-(. Non sono a conoscenza di puntatori automatici c ++ ampiamente utilizzati in grado di gestire cicli.

  • Forse c'è qualche oggetto di livello superiore che possiede tutti gli altri oggetti. Per esempio. spesso puoi dire che tutti i pezzi appartengono a un documento o un algoritmo o una transazione. Possono essere creati nel contesto di un oggetto di livello superiore e quindi eliminati automaticamente quando viene eliminato il loro oggetto di livello superiore (quando si rimuove il documento dalla memoria o si termina l'esecuzione dell'algoritmo). Ovviamente non puoi condividere pezzi tra oggetti di livello superiore.

Altri suggerimenti

Ci sono circa un milione (a una stima prudente) di approcci a questo. Stai davvero chiedendo & Quot; come posso progettare software in C ++ & Quot ;. E la risposta è, temo, & Quot; che cosa farà il tuo software? & Quot; - semplicemente sapere che vuoi trattare con persone e case non è sufficiente.

Non è questo il punto di OOP? Che cosa stai chiedendo è un dettaglio di implementazione, che nascondi dietro l'interfaccia pubblica di queste classi e quindi non devi preoccuparti, perché puoi cambiarlo senza cambiare l'interfaccia? Quindi vai avanti, provalo come suggerisci. Quindi, se si verificano prestazioni, memoria o altri problemi, è possibile correggere l'implementazione senza interrompere il resto del codice.

Mi sembra che l'archiviazione dei dati in un database e l'utilizzo di una sorta di mappatura relazionale agli oggetti, potrebbe essere un'altra opzione da guardare.

È possibile utilizzare boost :: shared_ptr per risolvere i problemi di memoria. Puoi quindi copiare liberamente shared_ptr in giro, restituirlo da funzioni, usarlo come variabile locale, ecc.

Un Person potrebbe quindi avere un std::map< string, boost::shared_ptr<Person> >, quindi X.getChild("George") cercherebbe semplicemente il bambino nella mappa e restituirebbe il puntatore. Penso che tu abbia capito il concetto, quindi lascerò il resto come esercizio per te;)

Jason, la mia fonte preferita su è il C ++ FAQ Book . Il problema è che stai effettivamente chiedendo & Quot; come posso usare C ++ per la programmazione orientata agli oggetti? & Quot;

Il meglio che posso dire in una risposta SO è questo:

tutte queste cose saranno classi in C ++, e le relazioni ecc assomiglieranno molto ai linguaggi di immondizia a cui sei abituato: se hai bisogno di una relazione tra una Persona e suo Figlio di nome " george " ;, scegli una struttura di dati che può memorizzare persone o bambini indicizzati per nome.

La gestione della memoria è in realtà più semplice che nella semplice C, se segui alcune regole: assicurati che tutti gli oggetti che ne hanno bisogno abbiano dei distruttori, assicurati che i distruttori ripuliscano tutto ciò che l'oggetto possiede, quindi assicurati di metterli sempre oggetti costruiti dinamicamente in contesti in cui escono dall'ambito quando non sono più necessari. Questi non copriranno tutti i casi, ma ti salveranno probabilmente dall'80% degli errori di allocazione della memoria.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top