Motivi legittimi per i riferimenti circolari in C ++

1

Ho un progetto scritto in C ++ su cui sto lavorando e che ha una relazione genitore-figlio in cui ogni bambino ha un solo genitore. Avevo precedentemente deciso dopo aver esaminato questo post che avrei fatto conoscere ai bambini niente dei loro genitori.

Un po 'di background sul mio progetto prima di entrare nella mia domanda: i genitori in questa situazione sono oggetti che rappresentano una raccolta di "blocchi" computazionali interconnessi. Il genitore (la classe "Modello") accetta uno o più valori di input, li esegue attraverso i suoi blocchi e i blocchi restituiscono uno o più valori di output. Gli oggetti figli sono i blocchi e un blocco è di proprietà di un solo modello. Il problema è che un certo tipo di blocco può contenere un modello in modo che io possa annidarli e riutilizzare i modelli. Sebbene la maggior parte dei blocchi non sappia nulla della classe Model, ModelBlock (che eredita Block) conosce i Modelli e può incapsulare un Modello.

La domanda: quanto sopra sembra funzionare abbastanza bene, almeno nella versione console della mia applicazione. Tuttavia, ho bisogno di una GUI per essere in grado di creare qualsiasi modello di grandi dimensioni senza perdere la testa e quindi ho iniziato a crearne uno. Il problema ha riacceso la sua brutta testa di nuovo quando ho capito che i Blocchi in generale hanno bisogno di conoscere il loro Modello in modo che ModelBlocks possa informare un Modello quando viene utilizzato all'interno di un altro Modello. Questo è così che posso facilmente trovare modelli "orfani" che non sono il modello radice e non usati in nessun altro contesto (nel senso che non verranno mai eseguiti). Immagino che potrei renderlo specifico per ModelBlocks, ma se ho intenzione di fare qualcosa del genere, perché non farlo applicare a tutti i blocchi?

In C #, la sua apparentemente non è una cattiva pratica usare un riferimento circolare per ottenere questo risultato (Entity Framework fa il wazoo ... causa devastazione con i serializzatori). Tuttavia, sembra molto tabù in C ++ utilizzare riferimenti circolari. Quindi, mi chiedo se quanto sopra è un uso legittimo. Se facessi questo, i Blocchi verrebbero informati della distruzione del Modello genitore in modo che sappiano se sono orfani. Anche se questo non è, sono gli usi legittimi per i riferimenti circolari in C ++ poiché sembrano essere relativamente comuni in altri linguaggi (ovviamente, supponendo che quei programmi in quelle altre lingue che usano riferimenti circolari non abbiano problemi di progettazione seri) ?

Se non ho spiegato abbastanza bene la mia domanda, fammelo sapere e proverò a chiarire.

EDIT: dovrei menzionare che le implementazioni effettive sono separate da tutto questo poiché utilizzo le interfacce (classi con tutte le funzioni virtuali pure) per definire come appare un blocco o come appare un modello.

Questo è un diagramma di classi parziale con le mie modifiche proposte: link

Ecco la fonte: link . Se hai voglia di costruirlo, dipende da Qt > = 4.7, boost > = 1.52. I progetti sono progetti Qt-Creator. Console e GUI dipendono dal motore.

    
posta Los Frijoles 18.02.2013 - 08:06
fonte

4 risposte

16

Non c'è niente di sbagliato nell'usare un puntatore non proprietario a qualcuno che ti richiama. Assicurati che non sia proprietario .

    
risposta data 18.02.2013 - 09:43
fonte
1

Puoi usare void * (o IUnknown * ) per ottenere un accoppiamento lento e ridurre la complessità del codice. Elimina i riferimenti circolari nei file di intestazione C ++.

// Model.h
class CModel
{
public:
    void addBlock(char *str, void** ppBlock);
};

// Block.h
class CBlock
{
public:
    void getParent(void** ppModel);
};

Quindi, con il cast appropriato, puoi ottenere ciò di cui hai bisogno, ad esempio:

#include "Model.h"
#include "Block.h"

// ...

CModel model;
CBlock *pBlock = NULL;
model.addBlock("something", (void**) &pBlock);

CModel *pParent = NULL;
pBlock->getParent((void**) &pParent);

Se aggiungi un sistema di conteggio dei riferimenti e dei puntatori intelligenti, allora avrai un ecosistema simile a ATL / COM.

    
risposta data 18.02.2013 - 15:29
fonte
0

Potresti voler utilizzare shared_ptr per possedere i link e weak_ptr per i link non proprietari (da figlio a padre). In questo modo puoi distruggere il genitore e il bambino può controllare se esiste un genitore.

    
risposta data 18.02.2013 - 09:56
fonte
-1

Sapete che ci sono problemi dove l'unica buona risposta è usare un sistema con una garbage collection migliore di qualsiasi altro prodotto di libreria C ++ portatile.

Potrei nominarne alcuni, ma sono piuttosto coinvolti. Esempi includono cose come prolog incorporato in C ++ e alberi di espressione usati in un compilatore.

Ho una libreria di raccolta dati di spazzatura e spazzatura portatile funzionante e il 90% è finito al momento. Poiché C ++ ha limitazioni non è illimitato come C # - la restrizione di base è che gli oggetti appartengono a un thread, ogni thread ha il proprio garbage collector ei riferimenti ai thread ACROSS devono ricadere su conteggi di riferimento gestiti atomicamente come shared_ptr e boost intrusive_ptr che coinvolgono la proprietà di più di una discussione non saranno raccolti. Inoltre, nella versione corrente, i puntatori deboli sono disponibili solo nel thread proprietario.

Dopo tanti pensieri, questi limiti sembrano il miglior compromesso possibile con una libreria C ++ portatile.

    
risposta data 11.05.2014 - 03:16
fonte

Leggi altre domande sui tag