codice ripetitivo su più classi. Il modo migliore per centralizzare il codice?

3

Sto lavorando con array 3D che applico in un array 1D con il seguente calcolo dell'indice:

index = x + WIDTH * (y + DEPTH * z)

Sto iniziando a notare che sto usando il calcolo dappertutto in diverse classi e mi chiedo se usare una macro sarebbe una buona soluzione?

Ho anche pensato di creare un metodo statico in una classe e di usarlo in tutte queste classi, ma aggiungere quella dipendenza a tutte quelle classi non mi sta bene.

Esiste una buona pratica standard o un modello da seguire in questo caso?

    
posta afuzzyllama 09.04.2015 - 18:22
fonte

3 risposte

12

Dato che la tua domanda è taggata , < em> non usare macro ! Dovrebbero solo essere usato per includere guardie e alcuni altri usi esoterici.

La soluzione corretta qui è una funzione inline indipendente. Supponendo che WIDTH e DEPTH siano costanti visibili alla funzione, ecco la sua definizione:

inline int f(int x, int y, int z) {
  return x + WIDTH * (y + DEPTH * z);
}

Inserisci questa funzione in un'intestazione accessibile da tutte le classi che la usano. Lo inserisco in un'intestazione con le funzioni di utilità per qualsiasi spazio dei nomi di cui fa parte. Se tale intestazione non esiste, questo potrebbe essere un buon momento per crearlo. Indipendentemente da ciò, deve andare in un'intestazione in modo che la definizione della funzione sia visibile a ciascuna unità di compilazione che include una chiamata alla funzione: in altre parole, il compilatore deve essere in grado di vedere la funzione per mettere i suoi contenuti in linea con il codice chiamante.

Questa soluzione centralizza il codice, fornendo tutti i vantaggi di una funzione. Poiché è in linea, rimuove la funzione chiamata overhead.

Soprattutto, fornisce semantica ben definita sui suoi parametri:

index = f(x++, ++y, z);

Questo è ben definito: possiamo cercare come funziona nello standard C ++. Se fosse una macro, potrebbe funzionare come previsto in un caso semplice ma potrebbe suddividersi in macro più complesse (oltre lo scopo di questa domanda).

    
risposta data 09.04.2015 - 18:40
fonte
1

but adding that dependency to all those classes doesn't sit well with me.

Se ogni array è incapsulato all'interno di una classe di dati, ha senso aggiungere un operatore sovraccarico a quella classe per cercare gli elementi.

// This code only illustrate the overloaded operator. 
// The code here is incomplete, and by itself is unfit 
// for all purposes. 
//
// (See below for a link to OpenCV's matrix source code.)
//
template <typename T>
class Array2
{
    T& operator () (int x, int y) { return mData[y * mWidth + x]; }
    const T& operator () (int x, int y) const { return mData[y * mWidth + x]; }
    // If one really needs a function to just return the value of (y * mWidth + x), such functions can also be added to this class.

private:
    int mWidth;
    int mHeight;
    T* mData; // make sure you know how to allocate, free, prevent memory leak, and prevent double-freeing of this pointer to array.
};

Una versione più completa sarà probabilmente simile a questa:

Se ci sono più classi, ed è prevedibile nel prossimo futuro che ogni classe possa avere presto dimensioni diverse (ad esempio, 2D e 4D), e se non si sta già utilizzando una classe per ogni tipo di matrice, si dovrebbe prendere seriamente in considerazione la possibilità di effettuare questa transizione.

Se, tuttavia, devi utilizzare una funzione gratuita (non un membro di alcuna classe), tieni presente questi aspetti:

  • Pensa a come WIDTH e HEIGHT sono passati alla funzione. Nell'esempio del codice (e anche nella risposta di @ Snowman), si presume che l'intero programma abbia un unico valore hardcoded per WIDTH e HEIGHT . Non appena inizi a utilizzare matrici di dimensioni diverse, vedrai che l'approccio di incapsulamento delle classi di dati è l'unico modo sensato per farlo.

  • Se hai bisogno di utilizzare matrici di dimensioni diverse, dai un nome distinto a quelle funzioni.

  • Evita le collisioni del nome della funzione. Se lo fai diventare un membro della classe, c'è meno preoccupazione per le collisioni dei nomi di funzioni. Altrimenti, puoi renderlo un membro di una classe statica, assegnare a tale funzione un prefisso specifico del progetto o metterla in uno spazio dei nomi.

Infine, va bene avere duplicati per pezzi così piccoli di codice. La prevenzione della duplicazione del codice dovrebbe essere soppesata rispetto al problema di aumentare le dipendenze (e quindi l'accoppiamento). In questo caso particolare, è opportuno lasciare che ogni classe abbia le funzioni di convenienza che usa.

Naturalmente, l'approccio incapsulato alla classe dei dati dovrebbe essere ancora l'approccio più favorevole, quando tutte le cose sono prese in considerazione.

Se nel futuro molto lontano inizi a considerare l'implementazione di SIMD e altre ottimizzazioni delle prestazioni, saranno necessarie ulteriori modifiche alla classe dati.

    
risposta data 10.04.2015 - 15:07
fonte
0

Oltre all'utilizzo di una funzione libera, potresti voler esaminare la scomposizione della tua classe. Se hai un sacco di classi che hanno una matrice 3D appiattita in una 1D e tutti accedono a tale matrice tramite questo calcolo, è probabile che abbiano tutti un class threeDinOneD comune che usano.

    
risposta data 09.04.2015 - 23:09
fonte

Leggi altre domande sui tag