MySQL Java JavaScript PHP Python HTML-CSS C-sharp C++ Go

Transmiterea argumentelor prin valoare și prin referință

Transmiterea argumentelor prin valoare

Argumentele, care reprezintă variabile sau constante, pot fi transmise unei funcții prin valoare (by value) sau prin referință (by reference).

La transmiterea prin valoare, funcția primește o copie a valorilor variabilelor și constantelor. De exemplu:

#include <iostream>
  
void square(int);   // prototipul funcției
 
int main()
{
    int n {4};
    std::cout << "Before square: n = " << n << std::endl;
    square(n);
    std::cout << "After square: n = " << n << std::endl;
}
 
void square(int m)
{
    m = m * m;
    std::cout << "In square: m = " << m << std::endl;
}

Funcția square primește un număr de tip int și îl ridică la pătrat. În funcția main, valoarea variabilei n este afișată înainte și după apelul funcției.

La execuție vom observa că modificarea parametrului m din funcția square nu afectează variabila n, ci doar copia ei:

Before square: n = 4  
In square: m = 16  
After square: n = 4

De ce se întâmplă asta? La compilare, pentru parametrii funcției se alocă locații separate de memorie. La apel, valorile argumentelor sunt evaluate și copiate în aceste locații. Funcția lucrează astfel cu copii ale valorilor, nu cu obiectele originale.

Transmiterea parametrilor prin referință

La transmiterea prin referință, se transmite o referință la obiect, astfel încât putem manipula chiar obiectul original. Să rescriem exemplul anterior folosind referință:

#include <iostream>
  
void square(int&);   // prototipul funcției
  
int main()
{
    int n {4};
    std::cout << "Before square: n = " << n << std::endl;
    square(n);
    std::cout << "After square: n = " << n << std::endl;
}
void square(int& m)
{
    m = m * m;
    std::cout << "In square: m = " << m << std::endl;
}

Acum parametrul m este transmis prin referință. Parametrul referință este legat direct de obiectul transmis, deci modificarea lui afectează obiectul original n.

Ieșirea va fi:

Before square: n = 4  
In square: m = 16  
After square: n = 16

Transmiterea prin referință permite returnarea mai multor valori din funcție și este mai eficientă pentru obiecte mari, deoarece se evită copierea.

Transmiterea referințelor ca argumente

Transmiterea unei referințe ca argument e diferită de transmiterea prin referință. De exemplu:

#include <iostream>
 
void square(int);   // prototipul funcției
  
int main()
{
    int n = 4;
    int &nRef = n;  // referință la variabila n
    std::cout << "Before square: n = " << n << std::endl;
    square(nRef);
    std::cout << "After square: n = " << n << std::endl;
}
void square(int m)
{
    m = m * m;
    std::cout << "In square: m = " << m << std::endl;
}

Dacă funcția primește argumente prin valoare, modificările nu afectează obiectele externe, chiar dacă transmiți referințe.

Ieșirea va fi:

Before square: n = 4  
In square: m = 16  
After square: n = 4

Transmiterea prin valoare e potrivită pentru obiecte mici, deoarece copierea valorilor e rapidă.

Transmiterea prin referință e recomandată pentru obiecte mari, fiind mai eficientă.

Conversia tipurilor

Există o altă diferență importantă: C++ poate converti automat valori dintr-un tip în altul (chiar dacă se pierde precizie, de exemplu double → int). Dar la transmiterea prin referință, conversiile implicite sunt excluse. De exemplu:

#include <iostream>
  
void printVal(int);
void printRef(int&);
 
int main()
{
    double value{3.14159};
    printVal(value);    // 3
    printRef(value);    // ! Eroare
}
void printVal(int n)
{
    std::cout << n << std::endl;
}
void printRef(int& n)
{
    std::cout << n << std::endl;
}

Sunt definite două funcții asemănătoare. printVal primește prin valoare, printRef prin referință. La ambele se transmite un double, dar parametrii sunt de tip int. La transmiterea prin valoare, conversia are loc (cu pierdere de precizie). La referință, compilatorul va da eroare. Acesta e un alt motiv pentru a prefera referințele – previn conversiile neintenționate de tipuri.