Quali sono i criteri decisionali in C ++ per creare qualcosa come membro dati o metodo virtuale?

5

(Ho pensato che fosse troppo soggettivo per SO, quindi postare qui ...)

Ho qualche comportamento che posso implementare in vari modi. Almeno due metodi sono mostrati nello snippet di codice qui sotto. Presumi che il membro dei dati m_well sia correttamente impostato in qualche modo al momento della costruzione dell'oggetto.

struct Behavior
{
    virtual bool behavesWell() { return true; }

private:
    bool m_well;
public:
    bool behavesMemberWell() { return m_well; }
};

struct OtherBehavior : public Behavior
{
    virtual bool behavesWell() { return false; }
};

Ovviamente quello si basa sulla distribuzione virtuale e l'altro si limita semplicemente al ritorno di un membro di dati.

Un terzo metodo non mostrato potrebbe avere la funzione membro pubblico non virtuale che non restituisce un membro dati fisso, ma invece chiama un virtuale - lasciamo da parte quello scopo per questo scopo.

Che cosa ti porterebbe all'uno o all'altro di questi due metodi di implementazione della funzionalità, in modo che un utente di questa classe possa interrogare il comportamento di un oggetto?

    
posta sdg 13.12.2010 - 20:52
fonte

6 risposte

3

Per quanto posso vedere, qui ci sono un paio di problemi in conflitto, che potrebbero essere chiariti se tu potessi dirci per quale scopo stai facendo questo? Il codice verrà utilizzato da te o dal tuo team o stai progettando un'API per il consumo da parte di altri?

Se stai solo cercando di progettare una buona interfaccia per le tue funzionalità, mettiti nei panni dei clienti delle tue funzioni, scrivi alcuni test unitari. Se durante la scrittura dell'unità test, tutto sembra imbarazzante o non ovvio, il tuo progetto deve migliorare.

Se la tua classe è pensata per la sottoclasse, allora non vuoi che membri di dati pubblici o membri di dati protetti siano per questo. I membri dati privati possono essere modificati in qualsiasi momento senza interrompere le sottoclassi, mentre le modifiche ai membri protetti (potenzialmente) interrompono le sottoclassi e le modifiche ai membri di dati pubblici (molto probabilmente) interrompono il codice client. Può sembrare noioso avere una coppia di metodi getter / setter per manipolare un membro dati, ma ne vale la pena se si desidera mantenere flessibile l'implementazione e non progettare un angolo.

I metodi virtuali indicherebbero che stai considerando di passare al client della tua classe un'interfaccia che sarebbe pura virtualità. Se questo è il caso, non puoi avere membri di dati in una classe astratta (pura virtuale), in modo che ti porti a metodi virtuali con chiamate di metodi virtuali per ottenere / impostare elementi di dati.

    
risposta data 14.12.2010 - 00:42
fonte
2

Abbiamo alcune classi che funzionano in questo modo - esiste un tipo di funzione che restituisce qualcosa di diverso in sottoclassi diverse. Il problema che ho è che non c'è un buon modo di dire all'interno del debugger, dato un BaseClass * .

Utilizza una variabile membro. Il prossimo a lavorare sul codice potrebbe ringraziarti.

    
risposta data 13.12.2010 - 23:21
fonte
0

Nel tuo esempio, utilizzerei un membro dati. È semplice e diretto.

Vorrei utilizzare un metodo virtuale in cui il comportamento della funzione cambia effettivamente nella classe derivata. Nel tuo esempio, sta ancora restituendo un valore booleano, anche se il valore è diverso. I metodi per me dovrebbero essere qualcosa che realizza - computazione o I / O o cosa hai - non solo essere un'interfaccia ottusa per i dati.

    
risposta data 13.12.2010 - 21:06
fonte
0

La regola generale è che tutti i membri dei tuoi dati dovrebbero essere privati. Ci sono delle eccezioni a questa regola, come le semplici strutture POD (plain old data) che vengono solo passate in giro o archiviate, ma il più delle volte ha senso mantenere privati tutti i membri dei dati.

Se è necessario fornire ad altri oggetti l'accesso in lettura a un membro dati, si scrive un getter. Se la tua classe non avrà classi derivate da essa, allora non è necessario rendere il getter virtuale. L'unica volta che è necessario rendere virtuale qualsiasi funzione membro, è quando si desidera consentire a una classe derivata di sovrascriverla.

    
risposta data 13.12.2010 - 22:00
fonte
0
  • virtual bool behavesWell() comporta un overhead di elaborazione extra ma, di solito, non è un metodo (ad esempio Funzioni e prestazioni virtuali - C ++ ).

  • bool behavesMemberWell() ha requisiti di archiviazione aggiuntivi: hai un bool m_well in più in ogni oggetto che crei. Con il primo approccio hai solo un puntatore aggiuntivo nel vtable (una volta per classe).

Se la classe Behavior può avere più di una funzione virtuale, preferirei il primo approccio (funzione virtuale).

    
risposta data 27.06.2015 - 16:37
fonte
0

Se voglio esporre o meno dati grezzi può essere determinato da una semplice regola: l'ambito / visibilità del membro dati. L'occultamento delle informazioni riguarda in definitiva la riduzione dell'ambito / visibilità per aiutare a mantenere invarianti. Naturalmente se hai una classe che espone membri di dati pubblici ma la classe è usata solo in una piccola parte del tuo sistema, allora l'ambito del membro dati è già molto piccolo. Cercare di ridurlo ulteriormente potrebbe essere eccessivo.

Dal momento che il tuo caso riguarda l'ereditarietà, devi mettere in discussione non solo il numero di posti in cui i campi di dati saranno esposti per quanto riguarda i client, ma anche il numero di sottoclassi che avrai da quando esporresti questo campo di dati per loro di manomettere pure.

Ora, in particolare per quanto riguarda l'invio dinamico, un consiglio generale che vorrei offrire è quello di mai preoccuparsi delle prestazioni del dispatch dinamico fino a quando non si dimostra effettivamente un hotspot. È uno dei modi più semplici per rendere il tuo codice inutilmente difficile da mantenere ossessionato dal costo delle chiamate di funzione indirette. Ovviamente std::sort è molto più veloce di qsort perché può evitare le chiamate di funzione indirette al comparatore, ma questo è un caso patologico in cui l'ordinamento di un milione di elementi potrebbe richiamare 50 milioni di chiamate indirette che eseguono tutte una piccola quantità di lavoro ciascuna.

Quindi la tua decisione qui dovrebbe avere ben poco a che fare con le prestazioni e molto altro da fare con la manutenibilità. Anche la funzione virtuale ha il vantaggio di permetterti di fare alcuni calcoli per determinare se questi comportamenti "si comportano bene". Non sono sicuro che sia un vantaggio pratico o meno nel tuo caso.

    
risposta data 17.12.2017 - 08:52
fonte

Leggi altre domande sui tag