Jaka jest różnica pomiędzy
- parametr przekazany przez odniesienie
- parametr przekazany przez wartość?
Czy mógłbyś podać jakieś przykłady?
Jaka jest różnica pomiędzy
Czy mógłbyś podać jakieś przykłady?
Odpowiedzi:
Przede wszystkim rozróżnienie „przekazywanie przez wartość vs. przekazywanie przez odniesienie” zdefiniowane w teorii CS jest obecnie nieaktualne, ponieważ technika pierwotnie zdefiniowana jako „przekazywanie przez odniesienie” od tego czasu nie sprzyjała i jest rzadko używana. 1
Nowsze języki 2 zwykle używają innej (ale podobnej) pary technik, aby osiągnąć te same efekty (patrz poniżej), co jest głównym źródłem zamieszania.
Drugim źródłem nieporozumień jest fakt, że w „przekazywaniu przez odniesienie” „odniesienie” ma węższe znaczenie niż ogólny termin „odniesienie” (ponieważ fraza poprzedza je).
Teraz autentyczna definicja to:
Gdy parametr jest przekazywany przez referencję , wywołujący i odbierający używają tej samej zmiennej dla parametru. Jeśli odbiorca modyfikuje zmienną parametru, efekt jest widoczny dla zmiennej dzwoniącego.
Gdy parametr jest przekazywany przez wartość , wywołujący i odbierający mają dwie niezależne zmienne o tej samej wartości. Jeśli odbiorca modyfikuje zmienną parametru, efekt nie jest widoczny dla dzwoniącego.
W tej definicji należy zwrócić uwagę na:
„Zmienna” oznacza tutaj samą zmienną wywołującą (lokalną lub globalną) - tzn. Jeśli przekażę zmienną lokalną przez odniesienie i przypiszę jej, zmienię samą zmienną wywołującą, a nie np. Cokolwiek, na co wskazuje, jeśli jest wskaźnikiem .
Znaczenie „odniesienia” w „przekazać przez odniesienie” . Różnica w stosunku do ogólnego terminu „odniesienie” polega na tym, że to „odniesienie” jest tymczasowe i dorozumiane. To, co w zasadzie otrzymuje callee, to „zmienna”, która jest w pewnym sensie „taka sama” jak pierwotna. To, jak konkretnie osiąga się ten efekt, jest nieistotne (np. Język może również ujawniać niektóre szczegóły implementacji - adresy, wskaźniki, dereferencje - to wszystko jest nieistotne; jeśli efekt netto jest taki, to jest to przejście przez odniesienie).
Teraz, w nowoczesnych językach, zmienne są zwykle „typami referencyjnymi” (inna koncepcja wymyślona później niż „przekazana przez referencję” i zainspirowana przez nią), tj. Rzeczywiste dane obiektowe są gdzieś przechowywane osobno (zwykle na stercie), i tylko „odniesienia” do niego są zawsze przechowywane w zmiennych i przekazywane jako parametry. 3)
Przekazanie takiego odniesienia mieści się w wartości przekazywanej, ponieważ wartość zmiennej jest technicznie samym odwołaniem, a nie obiektem, do którego się odnosi. Jednak efekt netto w programie może być taki sam, jak wartość przekazywana lub wartość referencyjna:
Jak widać, ta para technik jest prawie taka sama jak w definicji, tylko z poziomem pośrednim: wystarczy zastąpić „zmienną” „obiektem odniesienia”.
Nie ma dla nich żadnej uzgodnionej nazwy, co prowadzi do zniekształconych wyjaśnień, takich jak „wywołaj według wartości, gdzie wartość jest odniesieniem”. W 1975 r. Barbara Liskov zasugerowała termin „ współdzielenie obiektu przez obiekt ” (lub czasem po prostu „współdzielenie obiektu przez telefon”), chociaż nigdy się nie przyjął. Ponadto żadne z tych wyrażeń nie jest zbieżne z oryginalną parą. Nic dziwnego, że stare terminy zostały użyte ponownie w nieobecności czegoś lepszego, co doprowadziło do zamieszania. 4
UWAGA : Przez długi czas ta odpowiedź brzmiała:
Powiedz, że chcę udostępnić Ci stronę internetową. Jeśli podam ci adres URL, przekazuję przez referencję. Możesz użyć tego adresu URL, aby zobaczyć tę samą stronę internetową, którą widzę. Jeśli ta strona zostanie zmieniona, oboje zobaczymy zmiany. Jeśli usuniesz adres URL, wszystko, co robisz, to niszczenie odwołania do tej strony - nie usuwasz samej strony.
Jeśli wydrukuję stronę i dam ci wydruk, przekażę wartość. Twoja strona jest rozłączoną kopią oryginału. Nie zobaczysz żadnych kolejnych zmian, a wszelkie zmiany, które wprowadzisz (np. Bazgroły na wydruku) nie pojawią się na oryginalnej stronie. Jeśli zniszczysz wydruk, faktycznie zniszczyłeś kopię obiektu - ale oryginalna strona internetowa pozostaje nienaruszona.
Jest to w większości poprawne, z wyjątkiem węższego znaczenia „odniesienia” - jest ono zarówno tymczasowe, jak i niejawne (nie musi, ale jest wyraźne i / lub trwałe są dodatkowymi cechami, a nie częścią semantycznego przejścia przez odniesienie , jak wyjaśniono powyżej). Bliższą analogią byłoby przekazanie kopii dokumentu w porównaniu z zaproszeniem do pracy nad oryginałem.
1 O ile nie programujesz w Fortran lub Visual Basic, nie jest to zachowanie domyślne, aw większości języków we współczesnym użyciu prawdziwe wywołanie referencyjne nie jest nawet możliwe.
2 Wspiera go także spora liczba starszych
3 W kilku współczesnych językach wszystkie typy są typami referencyjnymi. To podejście zostało wprowadzone przez CLU w 1975 roku i od tego czasu zostało przyjęte przez wiele innych języków, w tym Python i Ruby. Wiele innych języków stosuje podejście hybrydowe, w którym niektóre typy są „typami wartości”, a inne są „typami referencyjnymi” - wśród nich są C #, Java i JavaScript.
4 Nie ma nic złego w recyklingu starego, odpowiedniego terminu jako takiego , ale trzeba jakoś wyjaśnić, które znaczenie jest używane za każdym razem. Nie robienie tego jest dokładnie tym, co powoduje zamieszanie.
Jest to sposób przekazywania argumentów do funkcji. Przekazywanie przez referencję oznacza, że parametr wywoływanych funkcji będzie taki sam, jak argument przekazanego argumentu (nie wartość, ale tożsamość - sama zmienna). Przekaż przez wartość oznacza, że parametr wywoływanych funkcji będzie kopią przekazanego argumentu wywołujących. Wartość będzie taka sama, ale tożsamość - zmienna - jest inna. Zatem zmiany parametru dokonanego przez wywoływaną funkcję w jednym przypadku zmieniają przekazany argument, aw drugim przypadku po prostu zmieniają wartość parametru w wywoływanej funkcji (która jest tylko kopią). W pośpiechu:
ref
używane w funkcji wywołującej i wywoływanej). Jon Skeet posiada również piękny wyjaśnienie tego tutaj .Kody
Ponieważ moim językiem jest C ++, użyję go tutaj
// 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);
}
A przykład w Javie nie zaszkodzi:
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
Ten facet prawie to przybija:
Wiele odpowiedzi tutaj (a zwłaszcza najbardziej wysoko ocenionych) jest niepoprawnych pod względem faktycznym, ponieważ źle rozumieją, co tak naprawdę oznacza „wywołanie przez odniesienie”. Oto moja próba uporządkowania spraw.
Mówiąc najprościej:
W kategoriach metaforycznych:
Zauważ, że obie te koncepcje są całkowicie niezależne i ortogonalne od koncepcji typów referencyjnych (która w Javie jest wszystkimi typami, które są podtypami Object
, a w C # wszystkie class
typy), lub koncepcji typów wskaźników jak w C (które są semantycznie równoważne na „typy referencyjne” Javy, po prostu z inną składnią).
Pojęcie typu odwołania odpowiada adresowi URL: zarówno on sam, jak i informacja, i jest odniesieniem ( wskaźnikiem , jeśli wolisz) do innych informacji. Możesz mieć wiele kopii adresu URL w różnych miejscach i nie zmieniają one witryny, do której prowadzą wszystkie linki; jeśli witryna zostanie zaktualizowana, każda kopia adresu URL będzie nadal prowadzić do zaktualizowanych informacji. I odwrotnie, zmiana adresu URL w jednym miejscu nie wpłynie na żadną inną pisemną kopię adresu URL.
Zauważ, że C ++ ma pojęcie „referencje” (np. int&
), Które nie jest podobne do „typów referencji” Java i C #, ale jest jak „wywołanie przez referencję”. „Typy referencyjne” Java i C # oraz wszystkie typy w Pythonie przypominają to, co C i C ++ nazywają „typami wskaźników” (np int*
.).
OK, oto dłuższe i bardziej formalne wyjaśnienie.
Na początek chcę podkreślić kilka ważnych fragmentów terminologii, aby pomóc wyjaśnić moją odpowiedź i upewnić się, że wszyscy odwołujemy się do tych samych pomysłów, gdy używamy słów. (W praktyce uważam, że zdecydowana większość nieporozumień związanych z takimi tematami wynika z używania słów w sposób, który nie do końca przekazuje zamierzone znaczenie).
Na początek przykład w deklaracji funkcji w języku C podobnym do C:
void foo(int param) { // line 1
param += 1;
}
A oto przykład wywołania tej funkcji:
void bar() {
int arg = 1; // line 2
foo(arg); // line 3
}
Korzystając z tego przykładu, chcę zdefiniować kilka ważnych fragmentów terminologii:
foo
jest funkcją zadeklarowaną w wierszu 1 (Java nalega na stworzenie metod wszystkich funkcji, ale koncepcja jest taka sama bez utraty ogólności; C i C ++ wprowadzają rozróżnienie między deklaracją a definicją, do której nie będę tutaj wchodził)param
to parametr formalny do foo
zadeklarował również na linii 1arg
jest zmienną , w szczególności lokalną zmienną funkcji bar
, zadeklarowaną i zainicjowaną w wierszu 2arg
jest również argumentem do konkretnego wywołania z foo
na linii 3Istnieją dwa bardzo ważne zestawy pojęć do rozróżnienia. Pierwszy to wartość w porównaniu ze zmienną :
bar
powyższej funkcji po wierszu int arg = 1;
wyrażenie arg
ma wartość 1
.final
lub C # readonly
) lub głęboko niezmienna (np. Przy użyciu C ++ const
).Inną ważną parą pojęć do rozróżnienia jest parametr kontra argument :
W wywołaniu według wartości formalne parametry funkcji są zmiennymi, które są nowo tworzone dla wywołania funkcji i które są inicjowane wartościami ich argumentów.
Działa to dokładnie tak samo, jak wszelkie inne rodzaje zmiennych są inicjowane wartościami. Na przykład:
int arg = 1;
int another_variable = arg;
Tutaj arg
i another_variable
są całkowicie niezależnymi zmiennymi - ich wartości mogą się zmieniać niezależnie od siebie. Jednak w punkcie, w którym another_variable
jest zadeklarowane, jest on inicjowany, aby przechowywał tę samą wartość, która arg
posiada - czyli jest 1
.
Ponieważ są to zmienne niezależne, zmiany, another_variable
które nie mają wpływu na arg
:
int arg = 1;
int another_variable = arg;
another_variable = 2;
assert arg == 1; // true
assert another_variable == 2; // true
Jest to dokładnie to samo, co związek między arg
i param
w naszym powyższym przykładzie, który powtórzę tutaj dla symetrii:
void foo(int param) {
param += 1;
}
void bar() {
int arg = 1;
foo(arg);
}
Jest tak, jakbyśmy napisali kod w ten sposób:
// entering function "bar" here
int arg = 1;
// entering function "foo" here
int param = arg;
param += 1;
// exiting function "foo" here
// exiting function "bar" here
Oznacza to, że cechą charakterystyczną tego, co oznacza wywołanie przez wartość , jest to, że odbiorca ( foo
w tym przypadku) otrzymuje wartości jako argumenty, ale ma własne oddzielne zmienne dla tych wartości ze zmiennych wywołującego ( bar
w tym przypadku).
Wracając do mojej metafory powyżej, jeśli jestem bar
, a ty foo
, gdy wzywam was, oddam ci kawałek papieru z wartością na nim napisane. Nazywasz ten kawałek papieru param
. Ta wartość jest kopią wartości, którą zapisałem w zeszycie (moje zmienne lokalne) w zmiennej, którą wywołuję arg
.
(Nawiasem mówiąc : w zależności od sprzętu i systemu operacyjnego istnieją różne konwencje wywoływania dotyczące sposobu wywoływania jednej funkcji z drugiej. Konwencja wywoływania przypomina naszą decyzję, czy zapisać wartość na kartce papieru, a następnie przekazać ją tobie , lub jeśli masz kawałek papieru, na którym piszę, lub jeśli piszę go na ścianie przed nami obojgiem. To także interesujący temat, ale daleko poza zakresem tej i tak już długiej odpowiedzi.)
W wywołaniu przez odniesienie parametry formalne funkcji są po prostu nowymi nazwami dla tych samych zmiennych, które wywołujący podaje jako argumenty.
Wracając do naszego przykładu powyżej, jest to równoważne z:
// entering function "bar" here
int arg = 1;
// entering function "foo" here
// aha! I note that "param" is just another name for "arg"
arg /* param */ += 1;
// exiting function "foo" here
// exiting function "bar" here
Ponieważ param
jest to po prostu inna nazwa arg
- to znaczy, że są tą samą zmienną , w której zmiany param
są odzwierciedlone arg
. Jest to podstawowy sposób, w jaki połączenie przez odniesienie różni się od połączenia według wartości.
Bardzo niewiele języków obsługuje wywołanie przez odniesienie, ale C ++ może to zrobić w następujący sposób:
void foo(int& param) {
param += 1;
}
void bar() {
int arg = 1;
foo(arg);
}
W tym przypadku, param
nie tylko mają taką samą wartość , jak arg
to faktycznie jest arg
(tylko pod inną nazwą), a więc bar
można zauważyć, że arg
została zwiększona.
Zauważ, że nie działa to w żaden sposób z Java, JavaScript, C, Objective-C, Python ani prawie żadnego innego popularnego języka. Oznacza to, że te języki nie są wywoływane przez odniesienie, są wywoływane według wartości.
Jeśli masz wywołanie według wartości , ale rzeczywista wartość jest typem referencyjnym lub wskaźnikiem , to sama „wartość” nie jest zbyt interesująca (np. W C jest to liczba całkowita o rozmiarze specyficznym dla platformy) - co to jest ciekawe jest to, na co wskazuje ta wartość .
Jeśli to, na co wskazuje ten typ odniesienia (czyli wskaźnik), jest zmienne, możliwy jest interesujący efekt: możesz zmodyfikować wartość wskazywaną, a dzwoniący może obserwować zmiany wartości wskazywanej, nawet jeśli dzwoniący nie może obserwować zmiany w samym wskaźniku.
Aby ponownie pożyczyć analogię adresu URL, fakt, że dałem ci kopię adresu URL do strony internetowej, nie jest szczególnie interesujący, jeśli chodzi o stronę, a nie o adres URL. Fakt, że zapisujesz swoją kopię adresu URL, nie ma wpływu na moją kopię adresu URL, nie jest to dla nas ważne (w rzeczywistości w językach takich jak Java i Python „URL” lub wartość typu odwołania może wcale nie będzie modyfikowany, tylko to, na co wskaże, może).
Barbara Liskov, gdy wynalazła język programowania CLU (który miał taką semantykę), zdała sobie sprawę, że istniejące terminy „call by value” i „call by reference” nie były szczególnie przydatne do opisu semantyki tego nowego języka. Wymyśliła więc nowy termin: połączenie przez udostępnianie obiektów .
Podczas omawiania języków, które technicznie nazywane są wartością, ale tam, gdzie często używane są typy referencyjne lub wskaźnikowe (to znaczy: prawie każdy współczesny język programowania imperatywny, obiektowy lub oparty na wielu paradygmatach), uważam, że jest to mniej mylące po prostu unikaj mówienia o połączeniu według wartości lub połączeniu przez odniesienie . Trzymaj się połączenia przez udostępnianie obiektu (lub po prostu zadzwoń przez obiekt ) i nikt się nie pomyli. :-)
The first is value versus variable.
The other important pair of concepts to distinguish is parameter versus argument:
Przed zrozumieniem 2 warunków MUSISZ zrozumieć następujące. Każdy obiekt ma 2 rzeczy, dzięki którym można go rozróżnić.
Więc jeśli powiesz employee.name = "John"
wiedz, że są 2 rzeczy name
. Jej wartość, która jest "John"
i również jego położenie w pamięci, który jest jakiś numer szesnastkowy może tak: 0x7fd5d258dd00
.
W zależności od architektury języka lub typu (klasy, struktury itp.) Twojego obiektu, możesz przenieść "John"
lub0x7fd5d258dd00
Przekazywanie "John"
jest znane jako przekazywanie według wartości. Przekazywanie 0x7fd5d258dd00
jest znane jako przekazywanie przez odniesienie. Każdy, kto wskazuje na tę lokalizację pamięci, będzie miał dostęp do wartości "John"
.
Aby uzyskać więcej informacji na ten temat, polecam przeczytać o dereferencjowaniu wskaźnika i dowiedzieć się, dlaczego wybrać struct (typ wartości) zamiast klasy (typ odwołania)
Oto przykład:
#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
}
y
został już ustawiony na 2 w poprzednim wierszu. Dlaczego miałoby wrócić do 0?
Najprostszym sposobem na uzyskanie tego jest plik Excel. Powiedzmy na przykład, że masz dwie liczby, 5 i 2 odpowiednio w komórkach A1 i B1, i chcesz znaleźć ich sumę w trzeciej komórce, powiedzmy A2. Możesz to zrobić na dwa sposoby.
Albo przekazując ich wartości do komórki A2 , wpisując = 5 + 2 w tej komórce. W takim przypadku, jeśli zmienią się wartości komórek A1 lub B1, suma w A2 pozostaje taka sama.
Lub przekazując „referencje” komórek A1 i B1 do komórki A2 , wpisując = A1 + B1 . W takim przypadku, jeśli zmienią się wartości komórek A1 lub B1, zmieni się również suma w A2.
Przekaż przez wartość wysyła KOPIA danych przechowywanych w określonej zmiennej, przekazanie przez odniesienie wysyła bezpośredni link do samej zmiennej. Więc jeśli przekażesz zmienną przez odniesienie, a następnie zmienisz zmienną w bloku, do którego ją przekazałeś, zmienna oryginalna zostanie zmieniona. Jeśli po prostu przekażesz wartość, pierwotna zmienna nie będzie mogła zostać zmieniona przez blok, w którym ją przekazałeś, ale dostaniesz kopię tego, co zawierała w momencie wywołania.
Przekaż według wartości - funkcja kopiuje zmienną i działa z kopią (więc nie zmienia niczego w oryginalnej zmiennej)
Przekaż przez referencję - funkcja używa oryginalnej zmiennej, jeśli zmienisz zmienną w innej funkcji, zmieni się ona również w oryginalnej zmiennej.
Przykład (skopiuj i użyj / wypróbuj to sam i zobacz):
#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;
}
Prosto, zerkaj. Ściany tekstu mogą być złym nawykiem.
Główną różnicą między nimi jest to, że zmienne typu wartości przechowują wartości, więc określenie zmiennej typu wartości w wywołaniu metody przekazuje kopię wartości tej zmiennej do metody. Zmienne typu odniesienia przechowują odniesienia do obiektów, więc określenie zmiennej typu odwołania jako argumentu przekazuje metodzie kopię faktycznego odwołania, które odnosi się do obiektu. Mimo że sama referencja jest przekazywana przez wartość, metoda może nadal używać referencji, którą otrzymuje, do interakcji z oryginalnym obiektem - i ewentualnie do modyfikacji. Podobnie, gdy zwracane są informacje z metody za pomocą instrukcji return, metoda zwraca kopię wartości przechowywanej w zmiennej typu wartości lub kopię odwołania przechowywaną w zmiennej typu odwołania. Po zwróceniu odwołania metoda wywołująca może użyć tego odwołania do interakcji z obiektem, do którego się odwołuje. Więc,
W c #, aby przekazać zmienną przez referencję, tak zwana metoda może modyfikować zmienną, C # zapewnia słowa kluczowe ref i out. Zastosowanie słowa kluczowego ref do deklaracji parametru pozwala przekazać zmienną do metody przez referencję - wywoływana metoda będzie mogła zmodyfikować oryginalną zmienną w wywołującym. Słowo kluczowe ref służy do zmiennych, które zostały już zainicjowane w metodzie wywoływania. Zwykle, gdy wywołanie metody zawiera niezainicjowaną zmienną jako argument, kompilator generuje błąd. Poprzedzenie parametru bez słowa kluczowego tworzy parametr wyjściowy. Wskazuje to kompilatorowi, że argument zostanie przekazany do wywoływanej metody przez odniesienie i że wywoływana metoda przypisze wartość oryginalnej zmiennej w wywołującym. Jeśli metoda nie przypisuje wartości do parametru wyjściowego w każdej możliwej ścieżce wykonania, kompilator generuje błąd. Zapobiega to również generowaniu przez kompilator komunikatu o błędzie dla niezainicjowanej zmiennej, która jest przekazywana jako argument do metody. Metoda może zwrócić tylko jedną wartość do swojego obiektu wywołującego za pomocą instrukcji return, ale może zwrócić wiele wartości poprzez określenie wielu parametrów wyjściowych (ref i / lub out).
patrz dyskusja c # i przykłady tutaj link tekst
Przykłady:
class Dog
{
public:
barkAt( const std::string& pOtherDog ); // const reference
barkAt( std::string pOtherDog ); // value
};
const &
jest ogólnie najlepszy. Nie ponosisz kary za budowę i zniszczenie. Jeśli odniesienie nie jest stałe, interfejs sugeruje, że zmieni przekazane dane.
Krótko mówiąc, wartość przekazywana to CO TO jest, a wartość referencyjna to GDZIE to jest.
Jeśli twoja wartość to VAR1 = „Happy Guy!”, Zobaczysz tylko „Happy Guy!”. Jeśli VAR1 zmieni się na „Happy Gal!”, Nie będziesz tego wiedział. Jeśli zostanie przekazany przez odniesienie, a VAR1 zmieni się, to zrobisz.
Jeśli nie chcesz zmieniać wartości oryginalnej zmiennej po przekazaniu jej do funkcji, funkcja powinna zostać zbudowana z parametrem „ pass by value ”.
Wtedy funkcja będzie miała TYLKO wartość, ale nie adres przekazywanej zmiennej. Bez adresu zmiennej kod wewnątrz funkcji nie może zmienić wartości zmiennej widzianej z zewnątrz funkcji.
Ale jeśli chcesz dać tej funkcji możliwość zmiany wartości zmiennej widzianej z zewnątrz, musisz użyć pass by reference . Ponieważ zarówno wartość, jak i adres (odniesienie) są przekazywane i dostępne wewnątrz funkcji.
pass by value oznacza, jak przekazać wartość do funkcji przy użyciu argumentów. przekazując wartość kopiujemy dane przechowywane w określonej przez nas zmiennej i jest ona wolniejsza niż przekazywanie przez referencję, ponieważ dane są kopiowane. z dokonanych zmian w skopiowanych danych nie wpłynie to na oryginalne dane. W drodze referencji lub adresu wysyłamy bezpośredni link do samej zmiennej. lub przekazanie wskaźnika do zmiennej. jest to szybsze, ponieważ zużywa mniej czasu
Oto przykład, który pokazuje różnice między przekazywaniem wartości - wartością wskaźnika - referencją :
void swap_by_value(int a, int b){
int temp;
temp = a;
a = b;
b = temp;
}
void swap_by_pointer(int *a, int *b){
int temp;
temp = *a;
*a = *b;
*b = temp;
}
void swap_by_reference(int &a, int &b){
int temp;
temp = a;
a = b;
b = temp;
}
int main(void){
int arg1 = 1, arg2 = 2;
swap_by_value(arg1, arg2);
cout << arg1 << " " << arg2 << endl; //prints 1 2
swap_by_pointer(&arg1, &arg2);
cout << arg1 << " " << arg2 << endl; //prints 2 1
arg1 = 1; //reset values
arg2 = 2;
swap_by_reference(arg1, arg2);
cout << arg1 << " " << arg2 << endl; //prints 2 1
}
Metoda „przejścia przez odniesienie” ma ważne ograniczenie . Jeśli parametr jest zadeklarowany jako przekazany przez referencję (więc jest poprzedzony znakiem &), odpowiadający mu parametr rzeczywisty musi być zmienną .
Rzeczywisty parametr odnoszący się do parametru formalnego „przekazany przez wartość” może być ogólnie wyrażeniem , więc można używać nie tylko zmiennej, ale również wyniku wywołania funkcji dosłownej lub nawet funkcji.
Funkcja nie może umieścić wartości w czymś innym niż zmienna. Nie może przypisać nowej wartości do literału ani zmusić wyrażenia do zmiany wyniku.
PS: Możesz także sprawdzić odpowiedź Dylana Beattie w bieżącym wątku, który wyjaśnia to prostymi słowami.