Implementare l'interfaccia usando i membri della classe

0

Questa era solo una domanda che mi stavo chiedendo in termini di best practice. Sto scrivendo un piccolo programma C ++ che usa un'interfaccia per implementare una funzione.

Nella classe che usa l'interfaccia (cioè eredita dalla classe di interfaccia), è per me una cattiva pratica usare le variabili membro così non devo continuare a ripeterle ogni volta che la funzione entra in ambito?

Supponiamo di avere questa interfaccia:

class base{
public:
    virtual ~base() {};
    virtual int method(int num) = 0;
};

E la seguente classe la eredita per implementare la funzione metodo .

class usesinterface : public base {
public:

    usesinterface() {};

    virtual int method(int num){ // Always adds 5 to the number
        return num_ + num; 
    }

private:
    int num_ = 5;
};

Memorizza num_ come cattiva pratica di un membro della classe o considerata errata? Ha più senso scrivere:

virtual int method(int num){
    const int number = 5;
    return number + num;
}

Sono consapevole che puoi solo scrivere + 5 per salvare la seccatura per questo esempio, ma questa è solo una versione semplificata di un problema che sto guardando.

MODIFICA 1:

In realtà, la variabile che sto memorizzando nella mia classe / ridefinendo ogni chiamata di funzione è una tabella di ricerca che viene utilizzata solo all'interno di questa chiamata di funzione singolare. Tuttavia, se lo metto nella funzione, la funzione stessa diventa molto prolissa (molto più leggibile la leggibilità del codice) e, senza un'ottimizzazione, credo che ridistribuirebbe continuamente la tabella di ricerca.

    
posta pointerlyfe 30.07.2018 - 09:59
fonte

3 risposte

2

Il problema è che è molto dipendente dalla situazione. Nello scenario di sketch, la variabile locale è decisamente preferita; non c'è motivo di avere un ambito di classe per questo.

Tuttavia, se _num è il risultato di un calcolo complesso di cui il risultato può essere memorizzato per un successivo riutilizzo, allora è una scelta ovvia memorizzarlo in una variabile membro, piuttosto che ricomprare tutto.

Generalmente uno dovrebbe preferire qualunque abbia il più piccolo ambito necessario, specialmente per le costanti. Se la situazione ti consente di evitare una serie di calcoli, non c'è motivo di non utilizzare un membro.

Modifica: basandosi sul tuo commento che si tratta di qualcosa come una grande tabella di ricerca, puoi sempre ridurre il cruft separando la tabella di ricerca in una funzione separata, isolando la tabella di ricerca. Cioè:.

static int lookupNum(int index)
{
    // Lookup table stuff
    return lookup_table[index];
}

int Foo::doStuff()
{
    // do some calculations
    return lookupNum(index);
}

Indipendentemente, dovrai posizionare la tabella di ricerca da qualche parte. Farne un membro non lo rende più leggibile. Hai semplicemente spostato la verbosità in un'altra posizione, dove è meno ovvio che sia utilizzata solo da una funzione.

    
risposta data 30.07.2018 - 10:11
fonte
0

La risposta è all'interno dei principi di programmazione orientata agli oggetti. Se num_ appartiene alla classe usesinterface , deve essere definita come una variabile di classe. Se non appartiene alla classe usesinterface e viene utilizzato per l'elaborazione di informazioni temporanee, deve essere definito nell'ambito di un corpo del metodo.

In un altro modo, se num_ definisce lo stato dell'oggetto, allora dovrebbe essere definito come una variabile di classe. C'è un'eccezione però. In alcune applicazioni complesse, è possibile dichiarare una variabile di classe solo per condividere il valore della variabile di classe attraverso chiamate di metodi pubbliche o private. In tal caso, gli sviluppatori usano variabili di classe. Tuttavia, se lo guardi, queste variabili di classe formano lo stato dell'oggetto.

    
risposta data 30.07.2018 - 10:07
fonte
0

Non utilizzare lo stato persistente a meno che non ne abbia bisogno per persistere.
I motivi per cui potresti aver bisogno sono:

  • Ricreare è troppo costoso, quindi è necessario il caching per soddisfare i requisiti di prestazione.
  • È modificabile dinamicamente e lasciare che il chiamante lo faccia passare è inappropriato. Non c'è modo di aggirarlo.

Nessuno di quelli sembra applicarsi qui, quindi non farlo.
Un constexpr (garantito valutato in fase di compilazione) potrebbe essere appropriato, o almeno un static const (valutare solo una volta, possibilmente al momento della compilazione, altrimenti al primo incontro se in una funzione o all'avvio se non lo è).

Per inciso, durante la lettura del tuo titolo ho pensato che stavi cercando di decidere di utilizzare funzioni libere o funzioni membro per la tua interfaccia, invece di come ereditare dalla tua classe base astratta. Inoltre, hai detto "usa" ma intendevi "ereditare" o almeno "implementare"; l'uso è normalmente dalla parte del consumatore anziché dall'implementatore.

Infine, il tuo ABC non dovrebbe essere né copiabile né mobile, sebbene i discendenti potrebbero esserlo. Quindi è necessario aggiungere:

protected:
    base(base const&) = default;
    base& operator=(base const&) = default;
    
risposta data 30.07.2018 - 17:05
fonte

Leggi altre domande sui tag