Ho bisogno di consigli per la progettazione di un sottosistema?

0

Lo sto facendo in C ++, non posso postare l'intera cosa, perché è gigantesco, lo riassumerò con un semplice esempio. Ho:

class B;

class A
{
 //Members and methods...
  void DoSomething(B* owningSystem);
};

class B
{
 //Members and methods
A memberA;
}

Quindi il problema è che A ha bisogno di sapere su B di usare il suo metodo DoSomething e A è in realtà un membro della BI avere una serie di B, il cui numero è determinato in fase di esecuzione. Questo potrebbe sembrare un esagerazione, ma Ho bisogno di un'ottimizzazione estrema in questo progetto specifico e mi stavo chiedendo - è meglio passare B * possedere il sistema o semplicemente memorizzare un puntatore di ogni B all'interno del suo membro A? Non penso che DoSomething sarà mai in linea, dal loro conteggio e le posizioni dei puntatori sono determinate in fase di esecuzione, ma posso scegliere di memorizzare un puntatore o di passarlo nella funzione. Devo minimizzare il sovraccarico a questo punto il più possibile, è fondamentale. Sono aperto a qualsiasi suggerimento , per favore

    
posta ulak blade 11.01.2014 - 01:52
fonte

2 risposte

2

Quello che ho trovato da tutti i tipi di prove è un errore: quando si hanno componenti con dipendenza circolare l'uno sull'altro (più in C ++ che in linguaggi dinamici come Python), si sta cercando un problema, quindi lo faccio sempre punto di definire sempre una stretta relazione gerarchica tra le classi.

In questo caso sembra che B sia una classe di livello più alto di A, quindi continuiamo così. Che ne dici di qualcosa di simile:

typedef interface struct;             // in MSVC this is built into compiler

interface IOwnerOfA {
    virtual int someFunctionCall() = 0;
}

class A {
    IOwnerOfA*  m_pOwner;

    void DoSomething() { .... bunch of code; m_pOwner->someFunctionCall(); .... }
}

class B : private IOwnerOfA {
    // yeah, cast is optional but I like them in these cases
    B() : m_a( static_cast< IOwnerOfA* >( this ) {}   

    virtual int someFunctionCall() { 
        ... provide specific functionality A needs;
        and nothing else;
    }

    A        m_a;
}

Quindi ora la gerarchia è IOwnerOfA < - A < --- B. Tutti conoscono l'interfaccia, B sa di A, A non sa nulla di B, quindi domani potresti avere C usando A senza dover fare eventuali modifiche in A.

Per quanto riguarda la tua domanda su come memorizzare una variabile membro o passare come parametro in DoSomething (), dipende solo da te. Se è solo un luogo isolato, probabilmente passerei a un parametro. Se la relazione tra classi è realmente che B è un "proprietario" di A e in A ha bisogno di richiamare in B (tramite l'interfaccia IOwnerOfA) in un numero di punti, quindi memorizzarlo come membro.

Un'altra alternativa da considerare (se si sta eseguendo una operazione in un unico punto) è invece di utilizzare interfacce e puntatori di classe, è possibile adottare un approccio funzionale e DoSomething () accetta std :: function come un tipo di parametro . Quindi utilizzare std :: bind () per passare in una funzione membro associata. Potrebbe sembrare un po 'folle, ma in realtà è piuttosto semplice (tranne che per gli errori del compilatore se si verifica qualcosa di sbagliato) e molto flessibile (una volta superati gli errori del compilatore)

Da aggiungere a ciò che @JTrana, ha commentato. Ci sono sicuramente dei casi in cui B possiede A e per qualsiasi motivo A ha bisogno di richiamare in B. Se è necessario, le uniche due opzioni che vedo utilizzano l'interfaccia (sì un livello extra di indiretto) e l'uso di tipi diretti (dipendenza circolare). ). Tra queste 2 scelte, la complessità extra dell'interfaccia è per me un vincitore. Tuttavia, questi casi veri sono pochi e lontani tra loro. La maggior parte delle volte potresti essere in grado di ridefinire la tua app in modo da non dover fare questo ciclo circolare.

Considerare se una parte di B che A ha bisogno possa essere convertita in una classe C più piccola:

class C {   // contains some commonly used functionality
}

class A {
    DoSomething( C* pCommonGuy) { .... }
}

class B {
    A  m_a;
    C  m_c;
}
    
risposta data 11.01.2014 - 07:38
fonte
0

Se B* è un membro di A, allora penso che avrai due dereferenze ogni volta che lo usi poiché m_b->Action(); è in realtà this->m_b->Action(); . Se si passa il puntatore come argomento, c'è un solo dereferenziamento.

È possibile che il passaggio dell'argomento potrebbe rallentare leggermente la chiamata della funzione a A::DoSomething() ma passare un singolo puntatore deve avere un sovraccarico quasi pari a zero.

    
risposta data 11.01.2014 - 07:26
fonte

Leggi altre domande sui tag