Lo stato interno "perde" quando influenza il comportamento esternamente visibile?

6

Ho un metodo (in C ++) che genera un valore basato su un parametro e i parametri delle chiamate precedenti. Chiamarlo più volte con lo stesso parametro può generare valori diversi ogni volta. Ad esempio:

public:

    int getValue(const int param)
    {
        int value = param ^ previousParam;
        previousParam = param;
        return value;
    }

private:

    int previousParam;

Ha senso permettere che questo metodo sia chiamato su const oggetti? previousParam parte dello stato interno dell'oggetto o "perdita" poiché lo stato interno influenza il comportamento esterno dell'oggetto?

Penso a questo metodo come un generatore che crea solo una serie di valori nelle chiamate successive, piuttosto che un metodo che modifica l'oggetto. Il chiamante non dovrebbe dipendere da un particolare parametro che genera un valore specifico. Sarebbe sensato cambiare le dichiarazioni al seguente?

int getValue(const int param) const;
mutable int previousParam;
    
posta M. Dudley 29.09.2014 - 17:15
fonte

4 risposte

7

Alcune buone risposte già qui, ma penso che ci sia qualcosa in più da dire sul tuo "modello mentale" dell'oggetto, dal momento che hai scritto

I think of this method like a generator that just creates a series of values on subsequent calls, rather than a method which modifies the object.

Ma dal momento che previousParam è un membro del tuo oggetto, il metodo effettivamente modifica l'oggetto. Tuttavia, nel tuo modello mentale, previousParam sembra non essere parte dell'oggetto. Quindi immagino che il tuo vero problema sia che l'intero metodo getValue che include il membro previousParam sia posizionato male - dovrebbe probabilmente essere parte di una classe diversa, che fornisce un'astrazione per il tuo generatore, e dove getValue è naturalmente un non -Con metodo

    
risposta data 29.09.2014 - 19:31
fonte
2

const solo "garantisce" che i membri di un oggetto non cambiano. Niente ti impedisce di creare metodi di const che dipendono dallo stato mutabile al di fuori dell'oggetto (ad esempio una variabile globale). Tuttavia, la maggior parte delle persone si aspetta che const significhi che lo stato dell'oggetto non cambierà quando si invocano metodi const su di esso. Dato che non devi conoscere i membri di un oggetto e dato che puoi imbrogliare con la parola chiave mutable , il suo stato è effettivamente quello che restituisce dai suoi metodi. Quindi un metodo const che cambia ciò che l'oggetto restituisce nelle chiamate future sicuramente sorprenderà le persone.

Se vuoi un "generatore" che restituisce una sequenza di valori, quindi restituisci un diverso oggetto non const (preferibilmente un iteratore) che contiene lo stato dell'oggetto originale.

    
risposta data 29.09.2014 - 18:44
fonte
2

Ci sono tre buoni motivi per non rendere getValue const.

  1. Principio di minimo stupore - Trovo molto, molto sorprendente scoprire che un metodo const ha causato un cambiamento esternamente visibile nel comportamento dell'oggetto (anche se non dovrei dipendere da un particolare parametro che genera un valore specifico).
  2. Logico contro fisico const - Si discute se const dovrebbe indicare costante logicamente (nessuna modifica nello stato o comportamento visibile) o costante fisicamente (identico bit a bit). getValue ovviamente non è fisicamente const, e poiché il comportamento visibile cambia, è difficile vedere come si possa creare un caso per const logico (anche se i chiamanti non dovrebbero dipendere da un particolare parametro che genera un valore specifico). / li>
  3. const in C ++ 11 - C ++ 11 ha rafforzato la semantica di const per indicare, approssimativamente, "utilizzabile in modalità thread-safe". (Questa è una semplificazione eccessiva; vedi qui , qui e qui per dettagli e discussioni.) getValue non è thread-safe e quindi non è const nel senso C ++ 11 si aspetta const a significare. (Sia che sia necessario rispettare questo significato di C ++ 11 di const dipende dall'applicazione.)
risposta data 29.09.2014 - 19:12
fonte
1

Penso che dipenda dalla semantica dell'oggetto.

Se si tratta di un generatore di numeri casuali o simile, RandGen dice, allora cosa significherebbe per un RandGen oggetto essere const ? L'oggetto è utilizzabile se fosse const ?

Ad esempio, potresti avere un metodo membro per impostare il seme, che dovrei considerare non- const . Quindi potrebbe avere senso che getValue sia const :

void setSeed(long seed);

Un altro esempio potrebbe essere una funzione che accetta un oggetto RandGen e controlla se il suo% interno% co_de è "sufficientemente casuale" . Probabilmente non vogliamo param per chiamare isSufficientlyRandom , quindi getValue potrebbe essere non- getValue qui:

bool isSufficientlyRandom(const RandGen& randGen) {
    return randGen.getPreviousParam() > 17; 
}
    
risposta data 29.09.2014 - 17:41
fonte

Leggi altre domande sui tag