Projít odkazem nebo kolem hodnoty?

hlasů
48

Když se učí nový programovací jazyk, jeden z možných překážek, které mohou nastat, je otázka, zda je jazyk, ve výchozím nastavení, pass-by-value nebo pass-by-reference .

Takže tady je moje otázka na vás všechny, ve svém oblíbeném jazyce, jak se to vlastně dělá? A jaké jsou možné úskalí ?

Váš oblíbený jazyk může být samozřejmě něco, co jste někdy hráli s: populární , temný , esoterický , nový , starý ...

Položena 05/08/2008 v 09:56
zdroj uživatelem
V jiných jazycích...                            


11 odpovědí

hlasů
31

Tady je můj vlastní příspěvek pro programovací jazyk Java .

První nějaký kód:

public void swap(int x, int y)
{
  int tmp = x;
  x = y;
  y = tmp;
}

volání této metody bude mít za následek následující:

int pi = 3;
int everything = 42;

swap(pi, everything);

System.out.println("pi: " + pi);
System.out.println("everything: " + everything);

"Output:
pi: 3
everything: 42"

i za použití ‚reálné‘ objekty se zobrazí podobný výsledek:

public class MyObj {
    private String msg;
    private int number;

    //getters and setters
    public String getMsg() {
        return this.msg;
    }


    public void setMsg(String msg) {
        this.msg = msg;
    }


    public int getNumber() {
        return this.number;
    }


    public void setNumber(int number) {
        this.number = number;
    }

    //constructor
    public MyObj(String msg, int number) {
        setMsg(msg);
        setNumber(number);
    }
}

public static void swap(MyObj x, MyObj y)
{
    MyObj tmp = x;
    x = y;
    y = tmp;
}

public static void main(String args[]) {
    MyObj x = new MyObj("Hello world", 1);
    MyObj y = new MyObj("Goodbye Cruel World", -1); 

    swap(x, y);

    System.out.println(x.getMsg() + " -- "+  x.getNumber());
    System.out.println(y.getMsg() + " -- "+  y.getNumber());
}


"Output:
Hello world -- 1
Goodbye Cruel World -- -1"

Takto je zřejmé, že Java předá jeho parametry podle hodnoty , jako hodnotu a všechno a objekty myObj nejsou prohozeny. si uvědomit, že „hodnotou“ je jediný způsob, jak v Javě předat parametry způsobu. (například jazyk C ++ umožňuje vývojáři k předání parametru odkazem použití ‚ a ‘ po typu parametru)

Nyní choulostivé části , nebo alespoň její část, která se bude plést většina nových Java vývojáře: (převzato z JavaWorld )
Původní autor: Tony Sintes

public void tricky(Point arg1, Point arg2)
{
    arg1.x = 100;
    arg1.y = 100;
    Point temp = arg1;
    arg1 = arg2;
    arg2 = temp;
}
public static void main(String [] args)
{
    Point pnt1 = new Point(0,0);
    Point pnt2 = new Point(0,0);
    System.out.println("X: " + pnt1.x + " Y: " +pnt1.y); 
    System.out.println("X: " + pnt2.x + " Y: " +pnt2.y);
    System.out.println(" ");
    tricky(pnt1,pnt2);
    System.out.println("X: " + pnt1.x + " Y:" + pnt1.y); 
    System.out.println("X: " + pnt2.x + " Y: " +pnt2.y);  
}


"Output
X: 0 Y: 0
X: 0 Y: 0
X: 100 Y: 100
X: 0 Y: 0"

choulostivé úspěšně změní hodnotu pnt1! To by znamenalo, že objekty jsou předány podle odkazu, to není tento případ! Správná tvrzení by bylo: na Objektové odkazy jsou předány podle hodnoty.

Více od Tony Sintes:

Metoda úspěšně změní hodnotu pnt1, i když je předáván hodnotou; nicméně, swap pnt1 a pnt2 selže! To je hlavním zdrojem zmatku. V metodě main (), pnt1 a pnt2 nejsou ničím jiným než objekt odkazy. Při předání pnt1 a pnt2 ke složité metodě (), Java předává reference od hodnoty, stejně jako jakýkoli jiný parametr. To znamená, že pokud jde o odkazy předány metody jsou vlastně kopie původních údajů. Na obrázku 1 jsou znázorněny dva odkazy ukazující na stejný objekt po Java předá objekt způsobu.

Obrázek 1 http://www.javaworld.com/javaworld/javaqa/2000-05/images/03-qa-0512-pass2b.gif

Závěr nebo dlouhý příběh krátký:

  • Java prochází to parametry podle hodnoty
  • „hodnotou“ je jediný způsob, jak do Java předání parametru způsobu
  • za použití metody z objektu zadaným jako parametr změní objekt jako odkazy poukazují na původní objekty. (v případě, že metoda sama mění některé hodnoty)

užitečné odkazy:

Odpovězeno 05/08/2008 v 09:56
zdroj uživatelem

hlasů
20

Zde je další článek pro programovací jazyk C #

c # předává své argumenty podle hodnoty (ve výchozím nastavení)

private void swap(string a, string b) {
  string tmp = a;
  a = b;
  b = tmp;
}

voláním této verzi swapu tedy nebude mít žádný výsledek:

string x = "foo";
string y = "bar";
swap(x, y);

"output: 
x: foo
y: bar"

Nicméně, na rozdíl od java c # dělá dát vývojář příležitost předat parametry podle odkazu , je to provedeno pomocí ‚ref‘ klíčové slovo před typem parametru:

private void swap(ref string a, ref string b) {
  string tmp = a;
  a = b;
  b = tmp;
} 

tato výměna se změní hodnota odkazované parametru:

string x = "foo";
string y = "bar";
swap(x, y);

"output: 
x: bar
y: foo"

c # má také mimo klíčové slovo , a rozdíl mezi ref a out je subtilní jedna. z MSDN:

Volající z způsobem, který bere out parametr není nutné přiřadit k proměnné předán jako out parametr před výzvou; Nicméně, je volaný nutné přiřadit out parametru před návratem.

a

Na rozdíl od parametry ref jsou považovány za nejprve přiřazena podle volaného. Jako takový, volaný se nemusí přiřadit k ref parametr před použitím. Ref parametry jsou předány i do a ven z metody.

malou chybou je, stejně jako v jazyce Java, který předměty předány hodnotou lze stále měnit pomocí své vnitřní metod

závěr:

  • c # předá jeho parametry, ve výchozím nastavení, podle hodnoty
  • ale když potřebné parametry mohou být také předány odkazem pomocí klíčového slova ref
  • vnitřní metod z parametru předán podle hodnoty změní objekt (v případě, že metoda sama mění některé hodnoty)

užitečné odkazy:

Odpovězeno 05/08/2008 v 17:40
zdroj uživatelem

hlasů
19

Python používá projít-by-value, ale protože všechny tyto hodnoty jsou objekt odkazy, čistý efekt je něco jako pass-by-reference. Nicméně, Python programátoři více přemýšlet o tom, zda typ objektu je proměnlivé nebo neměnná . Měnitelné objekty lze měnit na místě (např slovníky, seznamy, uživatelsky definované objekty), zatímco nezměnitelné objekty nemohou (například celá čísla, řetězce, n-tic).

Následující příklad ukazuje funkci, která je předán dva argumenty, neměnný řetězec, a proměnlivý seznam.

>>> def do_something(a, b):
...     a = "Red"
...     b.append("Blue")
... 
>>> a = "Yellow"
>>> b = ["Black", "Burgundy"]
>>> do_something(a, b)
>>> print a, b
Yellow ['Black', 'Burgundy', 'Blue']

Linka a = "Red"pouze vytvoří místní název a, pro hodnotu řetězce "Red"a nemá žádný vliv na kvalitu prošel-in argument (který je nyní skrytý, protože ase musí vztahovat k místní název od té doby). Přiřazení není operace na místě, bez ohledu na to, zda je tento argument proměnlivé nebo neměnné.

bParametr je odkaz na měnitelného seznamu objektu a .append()způsob provádí prodloužení na místě v seznamu, připínání na nové "Blue"hodnoty řetězce.

(Protože string objekty jsou neměnné, nemají žádné metody, které podporují změny v místě).

Jakmile funkce vrací, opětovné přiřazení anemá žádný účinek, zatímco rozšíření bjasně ukazuje, pass-by-referenčními sémantiky stylu volání.

Jak již bylo zmíněno dříve, a to i v případě, že argument aje proměnlivý druh, opětovné přiřazení v rámci funkce není operace na místě, takže tam by byl žádná změna hodnoty předané argumentu:

>>> a = ["Purple", "Violet"]
>>> do_something(a, b)
>>> print a, b
['Purple', 'Violet'] ['Black', 'Burgundy', 'Blue', 'Blue']

Pokud jste nechtěli svůj seznam upravený podle volané funkce, měli byste místo toho použít neměnný typ n-tice (identifikovanou závorkách v pravém formě, spíše než hranatých závorkách), který nepodporuje na místě .append()metodu:

>>> a = "Yellow"
>>> b = ("Black", "Burgundy")
>>> do_something(a, b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in do_something
AttributeError: 'tuple' object has no attribute 'append'
Odpovězeno 23/08/2008 v 17:50
zdroj uživatelem

hlasů
7

Protože jsem ještě neviděl Perl odpověď, myslel jsem, že bych psát jeden.

Pod kapotou, Perl pracuje efektivně jako pass-by-reference. Proměnné jako argumenty funkce volání jsou předány referenčně, konstanty jsou předány jako hodnoty jen pro čtení a výsledky výrazy jsou předány jako provizoria. Obvyklé idiomy postavit seznamy argumentů postoupením seznamu z @_, nebo shiftmají tendenci skrývat to od uživatele, dávat vzhled projít-by-value:

sub incr {
  my ( $x ) = @_;
  $x++;
}

my $value = 1;
incr($value);
say "Value is now $value";

To bude tisknout Value is now 1, protože $x++je zvýšen lexikální proměnné deklarované v rámci incr()funkce, spíše než proměnné předané. To pass-by-value styl je obvykle to, co je chtěl většinu času, protože funkce, které upravují jejich argumenty jsou vzácné v Perlu, a je třeba se vyhnout styl.

Pokud však z nějakého důvodu toto chování je specificky to žádoucí, může být dosaženo tím, působící přímo na prvky @_pole, protože budou jména pro proměnné předávaných do funkce.

sub incr {
  $_[0]++;
}

my $value = 1;
incr($value);
say "Value is now $value";

Tentokrát to bude tisknout Value is now 2, protože $_[0]++výraz zvýší skutečné $valueproměnné. Způsob, jakým to funguje tak, že pod kapotou @_není skutečná pole stejně jako většina ostatních polí (jako by byly získány my @array), ale jeho prvky jsou postaveny přímo ven z argumentů předaných volání funkce. To vám umožní postavit pass-by-reference sémantiku pokud to bude zapotřebí. Funkce volání argumenty, které jsou prosté proměnné jsou vloženy tak, jak je do tohoto pole a konstanty nebo výsledky složitějších výrazů, které znějí jako read-only provizoria.

Je však nesmírně vzácný, jak to udělat v praxi, protože Perl podporuje referenčních hodnot; to znamená, že hodnoty, které odkazují na jiné proměnné. Obvykle je mnohem jasnější postavit funkci, která má zřejmý vedlejší účinek na proměnnou, průchodem v odkazu k této proměnné. To je jasná indikace pro čtenáře na callsite, že projíždějících-referenční Sémantika jsou v platnosti.

sub incr_ref {
  my ( $ref ) = @_;
  $$ref++;
}

my $value = 1;
incr(\$value);
say "Value is now $value";

Zde \operátor dává odkaz v podstatě stejným způsobem jako &adresa-operátora v C.

Odpovězeno 13/04/2012 v 16:33
zdroj uživatelem

hlasů
6

Tam je dobré vysvětlení zde for .NET.

Mnoho lidí je překvapením, že referenční objekty jsou ve skutečnosti předány hodnotou (jak v jazyce C # a Java). Je to kopie zásobníku adresu. Tím se zabrání metodu měnit kde objekt skutečně připomíná, ale stále umožňuje metoda pro změnu hodnot objektu. V jazyce C # její možné předat odkaz ve formě odkazu, což znamená, že můžete změnit, pokud skutečný objekt bodů.

Odpovězeno 06/08/2008 v 23:43
zdroj uživatelem

hlasů
5

Nezapomeňte, že se také procházet podle jména , a kolem hodnotou-výsledkem .

Kolem hodnotového výsledek je podobný kolem hodnoty, s přidanou aspekt, že hodnota je nastavena v původní proměnné, který byl předán jako parametr. To může do jisté míry, nedocházelo k rušení globálních proměnných. To je zřejmě lepší v dělené paměti, kde nahrávka odkazem může způsobit neplatnost stránky ( reference ).

Přihrávka od názvu znamená, že hodnoty jsou vypočítány jen tehdy, pokud byly skutečně použity, spíše než na začátku procesu. Algol používá pass-by-jméno, ale zajímavý vedlejší efekt je, že je to velmi těžké napsat odkládací postup ( referenční ). Také výraz prošel podle názvu je přehodnoceny pokaždé, když je přístupná, což může mít i nežádoucí účinky.

Odpovězeno 05/08/2008 v 11:00
zdroj uživatelem

hlasů
4

Cokoli řeknete, jak projít-by-value nebo pass-by-reference musí být konzistentní v různých jazycích. Nejobvyklejší a konzistentní definice použity v různých jazycích je to, že se projíždějících-reference, můžete předat proměnnou funkce „normálně“ (tedy bez explicitně brát adresu nebo něco podobného), a funkci lze přiřadit k (ne mutovat obsah), je parametr uvnitř funkce a bude mít stejný účinek jako přiřazení do proměnné ve volajícím rozsahu.

Z tohoto pohledu jsou jazyky jsou seskupeny takto; přičemž každá skupina má stejný projíždějící sémantiku. Pokud si myslíte, že oba jazyky by neměly být ve stejné skupině, Vyzývám tě přijít s příkladem, který je odlišuje.

Drtivá většina jazyků, včetně C , Java , Python , Ruby , JavaScript , Scheme , OCaml , standard ML , Go , Objective-C , Smalltalk , atd. Jsou všechny projít-by-value pouze . Předáním hodnoty ukazatele (některé jazyky to „referenční“ říkají) se nepovažuje za přihrávku odkazem; Jsme jen obavy o věc prošla, ukazatel, nikoli věc ukázal.

Jazyky, jako je C ++ , C # , PHP , jsou ve výchozím nastavení pass-by-hodnotou, jako jazyky výše, ale funkce může explicitně deklarovat parametry jako pass-by-reference, pomocí &nebo ref.

Perl je vždy projít-by-reference; Nicméně, v praxi lidem téměř vždy zkopírovat hodnoty poté, co dostal ji tedy používat v pass-by-hodnotou způsobem.

Odpovězeno 13/04/2012 v 21:00
zdroj uživatelem

hlasů
4

hodnotou

  • je pomalejší než s odkazem, protože systém má kopírovat parametr
  • použita pouze jako vstup

Odkazem

  • rychleji, protože pouze ukazatel je předán
  • použita pro vstup a výstup
  • může být velmi nebezpečné, pokud jsou používány ve spojení s globální proměnné
Odpovězeno 05/08/2008 v 10:10
zdroj uživatelem

hlasů
3

Pokud jde o J , zatímco je pouze, Pokud vím, kolem hodnoty, je forma předávání odkazem, který umožňuje pohybující se velké množství dat. Jednoduše složit něco známý jako národní prostředí na sloveso (nebo funkce). Může to být instance třídy, nebo jen obecný kontejner.

spaceused=: [: 7!:5 <
exectime =: 6!:2
big_chunk_of_data =. i. 1000 1000 100
passbyvalue =: 3 : 0
    $ y
    ''
)
locale =. cocreate''
big_chunk_of_data__locale =. big_chunk_of_data
passbyreference =: 3 : 0
    l =. y
    $ big_chunk_of_data__l
    ''
)
exectime 'passbyvalue big_chunk_of_data'
   0.00205586720663967
exectime 'passbyreference locale'
   8.57957102144893e_6

Zjevnou nevýhodou je, že musíte znát název proměnné nějakým způsobem ve volané funkci. Ale tato technika může pohybovat velké množství dat bezbolestně. To je důvod, proč, zatímco technicky neprojde odkazem, to říkám „do značné míry, že“.

Odpovězeno 21/11/2009 v 18:14
zdroj uživatelem

hlasů
2

PHP je také předat podle hodnoty.

<?php
class Holder {
    private $value;

    public function __construct($value) {
        $this->value = $value;
    }

    public function getValue() {
        return $this->value;
    }
}

function swap($x, $y) {
    $tmp = $x;
    $x = $y;
    $y = $tmp;
}

$a = new Holder('a');
$b = new Holder('b');
swap($a, $b);

echo $a->getValue() . ", " . $b->getValue() . "\n";

výstupy:

a b

Nicméně v PHP4 předměty byly ošetřeny jako primitiv . Což znamená:

<?php
$myData = new Holder('this should be replaced');

function replaceWithGreeting($holder) {
    $myData->setValue('hello');
}

replaceWithGreeting($myData);
echo $myData->getValue(); // Prints out "this should be replaced"
Odpovězeno 11/08/2008 v 03:21
zdroj uživatelem

hlasů
-1

Ve výchozím nastavení je ANSI / ISO C používá buď - záleží na tom, jak deklarovat svou funkci, a jeho parametry.

Pokud deklarovat své funkční parametry jako ukazatele pak funkce bude pass-by-reference, a pokud deklarovat své funkční parametry jako ne-indikační proměnné pak funkce bude pass-by-value.

void swap(int *x, int *y);   //< Declared as pass-by-reference.
void swap(int x, int y);     //< Declared as pass-by-value (and probably doesn't do anything useful.)

Můžete dostat do problémů, pokud si vytvořit funkci, která vrací ukazatel na non-statické proměnné, která byla vytvořena v rámci této funkce. Vrácená hodnota následující kód bude nedefinovaný - není tam žádný způsob, jak zjistit, zda je paměťový prostor přidělené dočasné proměnné vytvořené ve funkci byl přepsán nebo ne.

float *FtoC(float temp)
{
    float c;
    c = (temp-32)*9/5;
    return &c;
}

Dalo by se však vrátí odkaz na statické proměnné nebo ukazatel, který byl předán v seznamu parametrů.

float *FtoC(float *temp)
{
    *temp = (*temp-32)*9/5;
    return temp;
}
Odpovězeno 13/01/2011 v 21:48
zdroj uživatelem

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more