Przekazywanie przez wartość i referencję

0

Witam, jeżeli jest to za niski poziom na dany dział to z góry przepraszam. Jestem nowy użytkownikiem... A więc do rzeczy.

Obecnie szkolę się za pomocą kursu z Shebang.pl i przerabiam sobie różne zadania, które były jakby "zadane" w tamtejszych artykułach.
W tej chwili zastanawiam się na poprawnością pewnego kodu. Moim zadaniem było zmodyfikowanie kodu źródłowego z przekazywaniem przez referencję na przekazywanie przez wartość.
Oryginalny kod z kursu:

#include <iostream>
using namespace std;
void zamien(double& a, double& b)
{
    double tymczasowa(a);      // Zmienna do tymczasowego przechowania wartości zmiennej a
    a = b;                     // Zastępujemy wartość zmiennej a wartością zmiennej b
    b = tymczasowa;            // Pobieramy wartość a ze zmiennej tymczasowej i zapisujemy ją w b
}

int main()
{
    double a(1.2), b(4.5);

    cout << "wartość a: " << a << ", wartość b: " << b << endl;

    zamien(a,b);   // Wywołanie funkcji

    cout << "wartość a: " << a << ", wartość b: " << b << endl;
    return 0;
}

Zmodyfikowany kod przeze mnie:

#include <iostream>
using namespace std;
double zamienA(double a, double b)
{
    double tymczasowa(a);      // Zmienna do tymczasowego przechowania wartości zmiennej a
    a = b;                     // Zastępujemy wartość zmiennej a wartością zmiennej b
    b = tymczasowa;             // Pobieramy wartość a ze zmiennej tymczasowej i zapisujemy ją w b
    double wynik;
    wynik = a;
    return wynik;
}
double zamienB(double a, double b)
{
    double tymczasowa(a);      // Zmienna do tymczasowego przechowania wartości zmiennej a
    a = b;                     // Zastępujemy wartość zmiennej a wartością zmiennej b
    b = tymczasowa;             // Pobieramy wartość a ze zmiennej tymczasowej i zapisujemy ją w b
    double wynik;
    wynik = b;
    return wynik;
}
int main()
{
    double a(1.2), b(4.5);

    cout << "wartość a: " << a << ", wartość b: " << b << endl;

    // zamien(a,b);   // Wywołanie funkcji

    double a1, b1;

    a1 = zamienA(a,b);
    b1 = zamienB(a,b);

    cout << "wartość a: " << a1 << ", wartość b: " << b1 << endl;
    return 0;
}

Co prawda, efekt działania jest poprawy. Jednakże nie mam pewności, co do poprawności kodu źródłowego - wiem, że da się to uprościć. Typowo jak totalny nowicjusz utrudniłem kod, dlatego bardzo zależy mi na poznaniu prostszej formy, która jest poprawna i wydajna.

Z góry dziękuję. :)

3
double zamienA(double a, double b)
{
    double tymczasowa(a);      // Zmienna do tymczasowego przechowania wartości zmiennej a
    a = b;                     // Zastępujemy wartość zmiennej a wartością zmiennej b
    b = tymczasowa;             // Pobieramy wartość a ze zmiennej tymczasowej i zapisujemy ją w b
    double wynik;
    wynik = a;
    return wynik;
}
double zamienB(double a, double b)
{
    double tymczasowa(a);      // Zmienna do tymczasowego przechowania wartości zmiennej a
    a = b;                     // Zastępujemy wartość zmiennej a wartością zmiennej b
    b = tymczasowa;             // Pobieramy wartość a ze zmiennej tymczasowej i zapisujemy ją w b
    double wynik;
    wynik = b;
    return wynik;
}

Cały ten kod mógłby być zastąpiony przez:

double zamienA(double a, double b)
{
    return b;
}
double zamienB(double a, double b)
{
    return a;
}

Natomiast implementowanie funkcji swap w tej sposób, tj. nie przez referencje, jest bardzo nienaturalne. Taki sposób jest kompletnie bez sensu.

0

Dzięki. Za słabo rozumiem funkcję mimo, że jakieś podstawy tam mam... Muszę więcej się nad tym skupić.

1

Nie, no przecież te referencje mają tu swój cel. Jeśli przekazujesz zmienną przez referencję, to zmieniasz tą zmienną (jej wartość) w ciele funkcji, tzn.:

 
void fooRef(int & a, int & b)
{
  a = 15;
  b = 0;
}

void foo(int a, int b)
{
  a = 15;
  b = 0;
}

void main()
{
  int var1 = 0;
  iny var2 = 1;

  fooRef(var1, var2);

  //jakie wartości będą miały tutaj var1 i var2?

  int zm1 = 0;
  int zm2 = 1;
  foo(zm1, zm2);

  //jakie wartości będą miały tutaj zm1 i zm2?
}

Po wykonaniu takiego kodu, var1 i var2 będą miały wartości 15 i 0. Dlatego, że do funkcji fooRef zostały przekazane przez referencję. Oznacza to, że funkcja fooRef pracuje BEZPOŚREDNIO na przekazanych do niej zmiennych. Teraz przyjrzyjmy się funkcji foo. Tutaj zmienne przekazujemy przez wartość. Po tej operacji będą miały wartości zachowane, tj. 0 i 1.

Przekazanie przez wartość oznacza stworzenie kopii tego, co przekazujesz. Czyli funkcja foo, zanim rozpocznie swoje działanie, to utworzy kopię przekazanych do niej argumentów i będzie pracować na tej kopii. Można to mniej więcej zilustrować tak:

 
void foo(int & a, int & b)
{
  int tempa = a;
  int tempb = b;

  tempa = 15;
  tempb = 0;
}

Co da taki sam efekt, jak:

void foo(int a, int b)
{
  a = 15;
  b = 0;
}

Jak widać z powyższych, przekazanie zmiennych przez wartość wymaga dodatkowej operacji - kopiowania tych zmiennych (dzieje się to oczywiście automatycznie). To oznacza, że funkcja, która przyjmuje parametry przez wartość będzie wolniejsza od tej, która przyjmuje parametry przez referencję. O ile przy takich prostych zmiennych nie ma to żadnego znaczenia, o tyle ma, jeśli przekazujemy duże obiekty lub tablice.

Więc kiedy przekazywać przez wartość:

  • gdy w funkcji MUSISZ pracować (zmieniać) na kopiach przekazanych parametrów.

Kiedy przez referencję:

  • gdy w funkcji MUSISZ pracować na oryginalnych zmiennych. Taka funkcje zmienia Ci zmienne, które do niej przekazałeś.

Istnieje jeszcze trzecia droga: przekazywanie przez stałą referencję:

 
void foo(const int & a)
{

}

Oznacza to, że przekazujesz zmienną przez referencję (czyli zyskujesz sybkość), ale nie możesz jej zmienić w ciele funkcji. A więc coś takiego się nawet nie skompiluje:

 
void foo(const int & a)
{
  a = 0;
}

Kiedy stosować stałą referencję?
Zawsze wtedy, gdy funkcja nie zmienia Ci przekazanych parametrów (np. tylko je odczytuje i na podstawie ich wartości przeprowadza swoje operacje). I to jest chyba najczęstszy przypadek.

4

Kiedy stosować stałą referencję?
Zawsze wtedy, gdy funkcja nie zmienia Ci przekazanych parametrów (np. tylko je odczytuje i na podstawie ich wartości przeprowadza swoje operacje). I to jest chyba najczęstszy przypadek.

yyy.. nie.

Przekazanie const int& nie ma sensu. Zwykłe int działa tak samo, a ma szansę być szybsze.

Przekazanie referencji na stałą (nie „stałej referencji”, bo w C++ referencje są zawsze stałe) ma sens, jeśli to jest obiekt, i przekazanie referencji jest tańsze (szybsze) niż kopiowanie obiektu. Np. w przypadku vector, string lepiej przekazać referencję niż cały kontener.

0

Dziękuję, te informację naprawdę mi się przydadzą w przyszłości. Ale ja po prostu wykonałem zadanie, które było przeznaczone pod koniec podziału z Funkcji w tym kursie. Co do postu Juhasa, czytałem już jak działa przekazywanie przez stałą referencję i tym podobne w tym kursie. Więc z pewnością ma rację. Wiadomo, to było tylko zwykłe ćwiczenie dla nowicjusza (może bezsensowne, ale uczy manipulować różnymi metodami), ale jak będę już na odpowiednim poziomie to tworząc własne programy poza obszarem "szkolenia" będę przede wszystkim zwracał uwagę na funkcjonalność, prostotę i wydajność. Jeszcze raz dziękuję za pomoc, przede mną trochę pracy. :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1