Domanda

class mystring {
 friend ostream& operator<<(ostream &out, const mystring ss) {
        out << ss.s;
        return out;
    }
private:
    string s;
public:
    mystring(const char ss[]) {
        cout << "constructing mystring : " << ss << endl;
        s = ss;
    }
};

void outputStringByRef(const mystring &ss) {
 cout << "outputString(const string& ) " << ss << endl;
}

void outputStringByVal(const mystring ss) {
 cout << "outputString(const string ) " << ss << endl;
}

int main(void) {
    outputStringByRef("string by reference");
    outputStringByVal("string by value");
    outputStringByRef(mystring("string by reference explict call mystring consructor"));
    outputStringByVal(mystring("string by value explict call mystring constructor"));
} ///:~

Considerando l'esempio precedente, non abbiamo potuto modificare la variabile passaggio per riferimento, né potremmo modificare l'uscita variable.The passaggio per valore di ogni metodi è same.Since non c'è differenza tra queste due modalità, perché ne C ++ supporta entrambi i metodi?

Grazie.

È stato utile?

Soluzione

C'è una differenza tra i due. Si consideri il seguente:

#include <iostream>
#include <string>
using std::string;

string g_value;

void callback() {
    g_value = "blue";
}

void ProcessStringByRef(const string &s) {
    callback();
    std::cout << s << "\n";
}

void ProcessStringByValue(const string s) {
    callback();
    std::cout << s << "\n";
}

int main() {
    g_value = "red";
    ProcessStringByValue(g_value);
    g_value = "red";
    ProcessStringByRef(g_value);
}

Output:

red
blue

Solo perché un riferimento è const all'interno di una funzione, non significa che il referand non può essere modificato tramite altri riferimenti (la situazione di un oggetto avere più riferimenti o puntatori ad esso è chiamato "aliasing"). Così v'è una differenza tra passando un riferimento const, e passando un valore const - nel caso del riferimento, l'oggetto potrebbe cambiare dopo che la chiamata viene effettuata. Nel caso del valore, il chiamato ha una copia privata, che non cambierà.

Dal momento che fanno cose diverse, C ++ permette di scegliere quali si desidera.

Non ci sono conseguenze per le prestazioni in entrambi i casi - quando si passa per valore, una copia deve essere fatta, che costa. Ma il compilatore sa poi che solo il tuo funzione può eventualmente avere alcun riferimento a tale copia, che potrebbe consentire ad altri ottimizzazioni. ProcessStringByRef non può caricare il contenuto della stringa per la stampa fino a quando callback() è tornato. ProcessStringByValue può, se il compilatore pensa agendo in tal modo è più veloce.

Di solito vi preoccupate per la copia, non l'ordine di esecuzione delle istruzioni, perché di solito la copia è il modo più costoso. Quindi di solito, si passa per riferimento, ove possibile, per gli oggetti che sono non banale per copiare. Ma la possibilità di aliasing a volte ha davvero gravi conseguenze per le prestazioni, evitando alcune ottimizzazioni anche se nessuno aliasing si verifica effettivamente. Ecco perché esistono "rigide regole aliasing", e la parola chiave restrict in C99.

Altri suggerimenti

f(const string&) prende stringa per riferimento const: fis operando direttamente sull'oggetto stringa passata per riferimento: v'è copia coinvolti. const impedisce modifiche all'oggetto originale però.

f(const string) assume un valore di stringa, che significa f riceve una copia della stringa originale. Anche se si elimina const, quando passa per valore, qualsiasi modifica alla stringa si perde quando i rendimenti f.

Non so esattamente cosa si intende per "Perché C ++ supporta entrambi i metodi?". E 'regole di sovraccarico solo generali che si applicano.

passaggio f(string s) per valore della stringa s, in altre parole, si crea una copia e l'inizializza con il valore della stringa si passa. Qualsiasi modifica alla copia, non sarà propagato alla stringa originale passato a chiamare la funzione. In f(const string s) const è ridondante, perché non è possibile modificare in ogni caso il valore originale.

Nel f(const string& s) invece la stringa non viene copiato, ma si passa un riferimento ad esso. Questo è di solito fatto quando si dispone di un oggetto di grandi dimensioni in modo che il "pass-by-value" in grado di produrre un overhead (è per questo che c ++ supporta entrambi i metodi). Il passaggio per riferimento significa che è possibile modificare il valore dell'oggetto "grandi" si passa, ma a causa del specificatore const, non è possibile modificarlo. Si tratta di una sorta di "protezione".

l'oggetto potrebbe contenere un mutabile membro, che possono essere modificati anche con un riferimento const

Con outputStringByRef è necessario assicurarsi che la variabile si sta passando un riferimento a rimane vivo e immutato per tutto il tempo outputStringByRef ne ha bisogno. con outputStringByVal la variabile che avete passato può morire o uscire dal campo di applicazione e la copia che la funzione ha è ancora ok.

Non v'è (quasi) nessuna differenza in termini di essere in grado di modificare la stringa all'interno della funzione. Tuttavia c'è una grande differenza in termini di ciò che viene passato. Il sovraccarico const mystring &ss accetta un riferimento const alla stringa. Anche se non può modificarlo, è la stessa memoria indirizzata. Se la stringa è lungo questo può essere un grande fattore (supponendo che la stringa non è implementato utilizzando copia -on-scrittura). Il modulo const mystring ss sta eseguendo una copia della stringa, memoria in modo diverso verrà affrontato.

In realtà la forma const mystring &ss potrebbe modificare la stringa, se è stato utilizzato un const_cast<mystring&> -. Anche se non vorrei raccomandare che qui

Immaginate di voler stampare un oggetto che non può essere copiato:

Thread th;
// ...
cout << th; // print out informations...

La versione di riferimento non copierà il filo, ma avrà l'indirizzo del th e creare un alias ad esso. L'altra versione avrebbe cercato di copiare il filo durante il passaggio per valore -. Ma la copia non può essere senseful per un tale oggetto (? Ci sarebbe un thread aggiuntivo allora)

Può aiutare a pensare di copiare un oggetto come clonazione di un oggetto . Copia th sopra in C ++ non significa semplicemente avere un altro manico allo stesso oggetto come in Java - vuol dire per clonare implicitamente l'oggetto indicato, e hanno tutta una copia di esso. Quindi la domanda è simile a "Perché Java supporta sia Object.clone e la copia dei riferimenti?" - entrambi hanno scopi diversi.

E poi c'è una questione di prestazioni troppo, dopo tutto. Se non si desidera copiare ogni volta che si passa qualcosa intorno per risorse oggetti affamati.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top