Esiste un uso per i parametri di riferimento non const?

0

Data una funzione come:

void do_stuff( Thing & thing )
{
    // at this point, I can inadvertently or purposefully change thing
}

C'è una ragione per cambiare intenzionalmente qualcosa? Cambiare cosa è possibile ma è sconsiderato?

    
posta user2738698 14.05.2015 - 22:12
fonte

4 risposte

10

Sì, quindi la funzione può cambiare thing .

In generale, è meglio mettere il comportamento sull'oggetto stesso. In altre parole, preferisci questo:

class Thing {
public:
  void do_stuff() {
    ...
  }
};

... a questo:

void do_stuff( Thing & thing ) {
  ...
}

Tuttavia, entrambi gli esempi sono perfettamente validi e utilizzabili. Un buon esempio di parametro di riferimento non const non membro è rappresentato dagli operatori di estrazione stream. Questa domanda di overflow dello stack va più nel dettaglio: Qualcuno usa effettivamente gli operatori di estrazione del flusso? Il generale l'idea è questa:

operator>>(std::istream &, Thing &)

La funzione non può esistere come parte della classe Thing perché la mano sinistra non è un oggetto della classe. Deve anche modificare un oggetto Thing in modo che il riferimento non possa essere const.

    
risposta data 14.05.2015 - 22:22
fonte
5

Ci sono dei motivi per cui potresti voler cambiare le cose sul posto. Una ragione potrebbe essere la prestazione: è costoso creare tutti questi nuovi oggetti per l'output.

Ci sono dei motivi per cui potresti scrivere cose in uno stile più funzionale, immutabile . Una ragione è trasparenza referenziale un'altra è la combinazione di funzioni e concorrenza più semplice e più affidabile.

Tutto è un compromesso. Come per la maggior parte delle cose nello sviluppo del software, la scelta giusta dipende dalle vostre esigenze specifiche. C ++ è un linguaggio multi-paradigma; ti permette di fare la scelta da solo.

    
risposta data 14.05.2015 - 22:19
fonte
3

Sì, alcune volte è molto conveniente essere in grado di modificare localmente un argomento pass-by-value in una funzione. Ad esempio, l'argomento potrebbe essere un riferimento a un nodo di un elenco collegato e all'interno della funzione potresti voler attraversare l'elenco, quindi vorrai fare node = *(node.next); .

Quindi, sconsiderato? sicuramente no, secondo me.

Esistono linguaggi che non ti permettono di modificare gli argomenti della funzione, (Pascal, penso?) che ti costringe a fare una copia dell'argomento in una variabile locale per modificarlo, e quindi il problema è che entrambi la copia e l'argomento originale sono in scope, quindi più in basso nel tuo codice potresti leggere erroneamente l'argomento al posto della variabile. Questo tipo di errore è probabilmente più probabile di una modifica involontaria dell'argomento. Quindi, consentendo all'argomento di cambiare, si elimina la variabile e quindi la possibilità di un tale errore. (Non mi preoccuperò nemmeno del problema di prestazioni, è trascurabile e persino ottimizzabile dal compilatore.)

Per quanto riguarda la possibilità di modificare erroneamente thing , questo è quello che ho da dire:

Nella mia esperienza, circa il 90% delle variabili membro, i parametri e le variabili locali che dichiariamo sono effettivamente const . ( final in java, readonly in C #.) Ciò significa che sebbene non li dichiariamo esplicitamente come const , li trattiamo come const : non cambiamo mai il loro valore dopo l'inizializzazione. Pertanto, spero che un linguaggio (* 1) venga creato un giorno in cui tutte le "variabili" saranno const a meno che non sia dichiarato esplicitamente di non essere const con l'uso di uno speciale "non -const "( mutable forse?) Parola chiave. In questo modo, nel caso raro che una variabile sia prevista che cambi, sarà immediatamente evidente nella sua dichiarazione, e il compilatore sarà anche in grado di emettere un avvertimento se dichiarate qualcosa come mutevole e dimenticate di cambiarlo. Ciò, naturalmente, avrà anche il vantaggio di impedirti di modificare involontariamente il tuo thing se non lo hai dichiarato come mutabile.

(* 1): una lingua imperativa , dal momento che si potrebbe sottolineare che i linguaggi funzionali già lo fanno.

    
risposta data 14.05.2015 - 23:45
fonte
0

Sì, come altri hanno già detto, da modificare.

Un motivo molto convincente per farlo è il polimorfismo. I riferimenti sono polimorfici, ma gli oggetti potrebbero slice .

Un altro è chiamare una funzione membro non const su un oggetto.

Un esempio:

class A
{
    public:
    virtual ~A() = default;
    virtual void someMethod() { printf("A::someMethod()"); }
};

class Subclass: public A
{
    public:
    void someMethod() override { printf("Subclass::someMethod()"); }
};

//always prints "A::someMethod()" because the object is sliced
void do_stuff_wrong( A thing )
{
    thing.someMethod();
}

void dont_do_stuff( const A & thing )
{
    //thing.someMethod();// compilation failure.
}

// do stuff can legally call someMethod() which is not marked const
void do_stuff( A & thing )
{
    // Yay! it calls out polymorphically
    thing.someMethod();
}

In questo esempio

    
risposta data 21.11.2018 - 23:31
fonte

Leggi altre domande sui tag