language-agnostic - variable - object passed by reference java




Qual è la differenza tra il passaggio per riferimento e il passaggio per valore? (12)

Qual è la differenza tra

  1. un parametro passato per riferimento
  2. un parametro passato per valore?

Potresti darmi qualche esempio, per favore?


È un modo per passare argomenti a funzioni. Passare per riferimento significa che il parametro delle funzioni chiamate sarà lo stesso dell'argomento passato dei chiamanti (non il valore, ma l'identità, la variabile stessa). Passa per valore significa che il parametro delle funzioni chiamate sarà una copia dell'argomento passato dei chiamanti. Il valore sarà lo stesso, ma l'identità - la variabile - è diversa. Così, le modifiche a un parametro eseguito dalla funzione chiamata in un caso modificano l'argomento passato e nell'altro caso cambiano semplicemente il valore del parametro nella funzione chiamata (che è solo una copia). In fretta:

  • Java supporta solo il passaggio per valore. Copia sempre gli argomenti, anche se durante la copia di un riferimento a un oggetto, il parametro nella funzione chiamata punterà allo stesso oggetto e le modifiche a tale oggetto verranno visualizzate nel chiamante. Dato che questo può essere fonte di confusione, here cosa Jon Skeet ha da dire su questo.
  • C # supporta il passaggio per valore e passa per riferimento (parola chiave ref utilizzata al chiamante e funzione chiamata). Anche Jon Skeet ha una bella spiegazione di questo here .
  • C ++ supporta il passaggio per valore e passa per riferimento (tipo di parametro di riferimento utilizzato nella funzione chiamata). Di seguito troverai una spiegazione di questo.

codici

Dato che il mio linguaggio è C ++, lo userò qui

// passes a pointer (called reference in java) to an integer
void call_by_value(int *p) { // :1
    p = NULL;
}

// passes an integer
void call_by_value(int p) { // :2
    p = 42;
}

// passes an integer by reference
void call_by_reference(int & p) { // :3
    p = 42;
}

// this is the java style of passing references. NULL is called "null" there.
void call_by_value_special(int *p) { // :4
    *p = 10; // changes what p points to ("what p references" in java)
    // only changes the value of the parameter, but *not* of 
    // the argument passed by the caller. thus it's pass-by-value:
    p = NULL;
}

int main() {
    int value = 10;
    int * pointer = &value;

    call_by_value(pointer); // :1
    assert(pointer == &value); // pointer was copied

    call_by_value(value); // :2
    assert(value == 10); // value was copied

    call_by_reference(value); // :3
    assert(value == 42); // value was passed by reference

    call_by_value_special(pointer); // :4
    // pointer was copied but what pointer references was changed.
    assert(value == 10 && pointer == &value);
}

E un esempio in Java non farà male:

class Example {
    int value = 0;

    // similar to :4 case in the c++ example
    static void accept_reference(Example e) { // :1
        e.value++; // will change the referenced object
        e = null; // will only change the parameter
    }

    // similar to the :2 case in the c++ example
    static void accept_primitive(int v) { // :2
        v++; // will only change the parameter
    }        

    public static void main(String... args) {
        int value = 0;
        Example ref = new Example(); // reference

        // note what we pass is the reference, not the object. we can't 
        // pass objects. The reference is copied (pass-by-value).
        accept_reference(ref); // :1
        assert ref != null && ref.value == 1;

        // the primitive int variable is copied
        accept_primitive(value); // :2
        assert value == 0;
    }
}

Wikipedia

http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_value

http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_reference

Questo ragazzo praticamente lo inchioda:

http://javadude.com/articles/passbyvalue.htm


Confronto: valore vs riferimento

Passa per valore I parametri locali sono copie degli argomenti originali passati nelle modifiche apportate nella funzione a queste variabili non influiscono sugli originali

Passa per riferimento I parametri locali sono riferimenti alle posizioni di archiviazione degli argomenti originali passati. Le modifiche a queste variabili nella funzione influiscono sugli originali Non viene eseguita alcuna copia, quindi viene salvato il sovraccarico di copia (tempo, spazio di archiviazione)


Ecco un esempio:

#include <iostream>

void by_val(int arg) { arg += 2; }
void by_ref(int&arg) { arg += 2; }

int main()
{
    int x = 0;
    by_val(x); std::cout << x << std::endl;  // prints 0
    by_ref(x); std::cout << x << std::endl;  // prints 2

    int y = 0;
    by_ref(y); std::cout << y << std::endl;  // prints 2
    by_val(y); std::cout << y << std::endl;  // prints 2
}

Esempi:

class Dog 
{ 
public:
    barkAt( const std::string& pOtherDog ); // const reference
    barkAt( std::string pOtherDog ); // value
};

const & è generalmente meglio. Non incorre nella pena di costruzione e distruzione. Se il riferimento non è const, la tua interfaccia suggerisce che cambierà i dati passati.


In breve, Passato dal valore è CHE COSA è e passato per riferimento è DOVE è.

Se il tuo valore è VAR1 = "Happy Guy!", Vedrai solo "Happy Guy!". Se VAR1 cambia in "Happy Gal!", Non lo saprai. Se è passato per riferimento e VAR1 cambia, lo farai.


Innanzitutto, la distinzione "passa per valore contro passa per riferimento" come definita nella teoria CS è ormai obsoleta perché la tecnica originariamente definita come "passaggio per riferimento" è ormai caduta in disgrazia e viene utilizzata raramente. 1

Le lingue più recenti 2 tendono ad utilizzare una coppia di tecniche diverse (ma simili) per ottenere gli stessi effetti (vedi sotto), che è la fonte principale di confusione.

Una fonte secondaria di confusione è il fatto che in "passaggio per riferimento", "riferimento" ha un significato più ristretto rispetto al termine generale "riferimento" (perché la frase lo precede).

Ora, la definizione autentica è:

  • Quando un parametro viene passato per riferimento , il chiamante e il destinatario ricevono la stessa variabile per il parametro. Se il callee modifica la variabile parametro, l'effetto è visibile alla variabile del chiamante.

  • Quando un parametro viene passato per valore , il chiamante e il chiamato hanno due variabili indipendenti con lo stesso valore. Se il callee modifica la variabile parametro, l'effetto non è visibile al chiamante.

Le cose da notare in questa definizione sono:

  • "Variabile" indica qui la variabile (locale o globale) del chiamante stesso - cioè se passo una variabile locale per riferimento e assegnale ad essa, cambierò la variabile stessa del chiamante, non ad es. A ciò a cui punta se è un puntatore .

    • Questo è ora considerato una cattiva pratica (come una dipendenza implicita). In quanto tali, praticamente tutte le lingue più recenti sono esclusivamente, o quasi esclusivamente, pass-by-value. Il pass-by-reference viene ora utilizzato principalmente sotto forma di "argomenti di output / inout" in lingue in cui una funzione non può restituire più di un valore.
  • Il significato di "riferimento" in "passa per riferimento" . La differenza con il termine generale "di riferimento" è che questo "riferimento" è temporaneo e implicito. In sostanza, ciò che il callee ottiene è una "variabile" che è in qualche modo "la stessa" di quella originale. Il modo in cui questo effetto viene raggiunto è irrilevante (ad esempio il linguaggio può anche esporre alcuni dettagli di implementazione - indirizzi, indicatori, dereferenziazioni - tutto ciò è irrilevante, se l'effetto netto è questo, è un riferimento pass-by).

Ora, nei linguaggi moderni, le variabili tendono ad essere di "tipi di riferimento" (un altro concetto inventato più tardi di "passare per riferimento" e ispirato da esso), cioè i dati oggetto reali vengono memorizzati separatamente da qualche parte (di solito, nell'heap), e solo i "riferimenti" ad esso sono mai contenuti in variabili e passati come parametri. 3

Il passaggio di un riferimento di questo tipo rientra in un valore pass-by perché il valore di una variabile è tecnicamente il riferimento stesso, non l'oggetto riferito. Tuttavia, l'effetto netto sul programma può essere uguale a quello del pass-by-value o del pass-by-reference:

  • Se un riferimento è preso dalla variabile del chiamante e passato come argomento, ha lo stesso effetto del pass-per-riferimento: se l'oggetto di riferimento è mutato nel callee, il chiamante vedrà la modifica.
    • Tuttavia, se una variabile che contiene questo riferimento viene riassegnata, smetterà di puntare a quell'oggetto, quindi qualsiasi ulteriore operazione su questa variabile influirà invece su qualsiasi cosa a cui punta ora.
  • Per avere lo stesso effetto del pass-by-value, ad un certo punto viene creata una copia dell'oggetto. Le opzioni includono:
    • Il chiamante può semplicemente fare una copia privata prima della chiamata e dare al callee un riferimento a quello.
    • In alcuni linguaggi, alcuni tipi di oggetto sono "immutabili": qualsiasi operazione su di essi che sembra modificare il valore crea effettivamente un oggetto completamente nuovo senza influire su quello originale. Quindi, passare un oggetto di questo tipo ad un argomento ha sempre l'effetto del valore pass-by: una copia per il callee verrà creata automaticamente se e quando è necessario un cambiamento, e l'oggetto del chiamante non sarà mai influenzato.
      • Nei linguaggi funzionali, tutti gli oggetti sono immutabili.

Come si può vedere, questa coppia di tecniche è quasi la stessa di quelle della definizione, solo con un livello di riferimento indiretto: basta sostituire "variabile" con "oggetto di riferimento".

Non esiste un nome concordato per loro, il che porta a spiegazioni contorte come "call by value dove il valore è un riferimento". Nel 1975, Barbara Liskov ha suggerito il termine " call-by-object-sharing " (o talvolta semplicemente "call-by-sharing") anche se non è mai stato preso in considerazione. Inoltre, nessuna di queste frasi disegna un parallelo con la coppia originale. Non c'è da stupirsi che i vecchi termini siano stati riutilizzati nell'assenza di qualcosa di meglio, portando a confusione. 4

NOTA : per molto tempo, questa risposta diceva:

Dire che voglio condividere una pagina web con te. Se ti dico l'URL, sto passando per riferimento. Puoi usare quell'URL per vedere la stessa pagina web che posso vedere. Se quella pagina è cambiata, entrambi vediamo le modifiche. Se elimini l'URL, tutto ciò che stai facendo sta distruggendo il tuo riferimento a quella pagina - non stai eliminando la pagina stessa.

Se stampo la pagina e ti do la stampa, sto passando per valore. La tua pagina è una copia disconnessa dell'originale. Non vedrai nessuna modifica successiva e le eventuali modifiche apportate (ad esempio scarabocchi sulla stampa) non verranno visualizzate nella pagina originale. Se distruggi la stampa, hai effettivamente distrutto la tua copia dell'oggetto - ma la pagina web originale rimane intatta.

Questo è per lo più corretto tranne il significato più ristretto di "riferimento" - essendo sia temporaneo che implicito (non è necessario, ma essendo esplicito e / o persistente sono funzionalità aggiuntive, non una parte della semantica pass-by-reference , come spiegato sopra). Un'analogia più vicina ti darebbe una copia di un documento vs invitandoti a lavorare sull'originale.

1 A meno che non si stia programmando in Fortran o Visual Basic, non è il comportamento predefinito, e nella maggior parte delle lingue nell'uso moderno, la vera chiamata per riferimento non è nemmeno possibile.

2 Anche una buona quantità di anziani lo supporta

3 In diverse lingue moderne, tutti i tipi sono tipi di riferimento. Questo approccio è stato sperimentato dal linguaggio CLU nel 1975 e da allora è stato adottato da molti altri linguaggi, tra cui Python e Ruby. E molte altre lingue usano un approccio ibrido, in cui alcuni tipi sono "tipi di valore" e altri sono "tipi di riferimento", tra cui C #, Java e JavaScript.

4 Non c'è niente di male con il riciclaggio di un vecchio termine adatto , ma bisogna in qualche modo chiarire quale significato viene usato ogni volta. Non farlo è esattamente ciò che continua a causare confusione.


Passa per valore - La funzione copia la variabile e funziona con una copia (quindi non cambia nulla nella variabile originale)

Passa per riferimento - La funzione usa la variabile originale, se cambi la variabile nell'altra funzione, cambia anche nella variabile originale.

Esempio (copia e usa / prova questo te stesso e vedi):

#include <iostream>

using namespace std;

void funct1(int a){ //pass-by-value
    a = 6; //now "a" is 6 only in funct1, but not in main or anywhere else
}
void funct2(int &a){ //pass-by-reference
    a = 7; //now "a" is 7 both in funct2, main and everywhere else it'll be used
}

int main()
{
    int a = 5;

    funct1(a);
    cout<<endl<<"A is currently "<<a<<endl<<endl; //will output 5
    funct2(a);
    cout<<endl<<"A is currently "<<a<<endl<<endl; //will output 7

    return 0;
}

Resta semplice, fa capolino. I muri di testo possono essere una cattiva abitudine.


Passa per valore invia una COPIA dei dati memorizzati nella variabile specificata, passa per riferimento invia un collegamento diretto alla variabile stessa. Quindi, se si passa una variabile per riferimento e quindi si modifica la variabile all'interno del blocco in cui è stata inserita, la variabile originale verrà modificata. Se passi semplicemente per valore, la variabile originale non potrà essere modificata dal blocco in cui l'hai passata, ma otterrai una copia di ciò che conteneva al momento della chiamata.


Prima di comprendere i 2 termini, DEVI capire quanto segue. Ogni oggetto ha 2 cose che possono renderlo distinto.

  • Il suo valore
  • Il suo indirizzo

Quindi se dici employee.name = "John"

sappi che ci sono 2 cose sul name . Il suo valore che è "John" e anche la sua posizione nella memoria, che è un numero esadecimale forse in questo modo: 0x7fd5d258dd00 .

A seconda dell'architettura della lingua o del tipo (classe, struct, ecc.) Del tuo oggetto, devi trasferire "John" o 0x7fd5d258dd00

Passare "John" è noto come passaggio per valore. Il passaggio 0x7fd5d258dd00 è noto come passaggio per riferimento. Chiunque indichi questa posizione di memoria avrà accesso al valore di "John" .

Per ulteriori informazioni su questo, ti consiglio di leggere sul dereferenziamento di un puntatore e anche sul perché scegliere struct (tipo di valore) rispetto alla classe (tipo di riferimento)


Quando si passa per ref si passa fondamentalmente un puntatore alla variabile. Passa per valore stai passando una copia della variabile. Nell'uso di base questo normalmente significa passare per modifiche di ref alla variabile che si vedrà essere il metodo di chiamata e passare per valore non lo faranno.


Una grande differenza tra loro è che le variabili di tipo value memorizzano i valori, quindi specificando una variabile di tipo value in una chiamata di metodo passa una copia del valore di quella variabile al metodo. Le variabili di tipo riferimento memorizzano i riferimenti agli oggetti, quindi la specifica di una variabile di tipo di riferimento come argomento passa al metodo una copia del riferimento effettivo che fa riferimento all'oggetto. Anche se il riferimento stesso viene passato per valore, il metodo può ancora utilizzare il riferimento che riceve per interagire con - e possibilmente modificare - l'oggetto originale. Analogamente, quando si restituiscono informazioni da un metodo tramite un'istruzione return, il metodo restituisce una copia del valore memorizzato in una variabile di tipo value o una copia del riferimento memorizzata in una variabile di tipo reference. Quando viene restituito un riferimento, il metodo chiamante può utilizzare tale riferimento per interagire con l'oggetto di riferimento. Quindi, in effetti, gli oggetti vengono sempre passati per riferimento.

In c #, per passare una variabile per riferimento in modo che il metodo chiamato possa modificare la variabile, C # fornisce le parole chiave ref e out. L'applicazione della parola chiave ref a una dichiarazione di parametro consente di passare una variabile a un metodo per riferimento: il metodo chiamato sarà in grado di modificare la variabile originale nel chiamante. La parola chiave ref viene utilizzata per le variabili che sono già state inizializzate nel metodo di chiamata. Normalmente, quando una chiamata al metodo contiene una variabile non inizializzata come argomento, il compilatore genera un errore. Precedere un parametro con la parola chiave out crea un parametro di output. Ciò indica al compilatore che l'argomento verrà passato al metodo chiamato per riferimento e che il metodo chiamato assegnerà un valore alla variabile originale nel chiamante. Se il metodo non assegna un valore al parametro di output in ogni possibile percorso di esecuzione, il compilatore genera un errore. Ciò impedisce inoltre al compilatore di generare un messaggio di errore per una variabile non inizializzata che viene passata come argomento a un metodo. Un metodo può restituire un solo valore al proprio chiamante tramite un'istruzione return, ma può restituire molti valori specificando più parametri output (ref e / o out).

vedi c # discussione ed esempi qui link text


passare per valore significa come passare il valore a una funzione facendo uso di argomenti. Passando per valore, copiamo i dati memorizzati nella variabile che specifichiamo ed è più lento di passare per riferimento se i dati vengono copiati. di apportare modifiche nei dati copiati i dati originali non sono interessati. nd in pass per refernce o per indirizzo inviamo il link diretto alla variabile stessa. o passare il puntatore a una variabile. è più veloce perché si consuma meno tempo





pass-by-value