Przekazywanie parametrów do funkcji w C++. Ściągawka

Kontynuując temat z poprzedniego wpisu, Wskaźniki w C++, referencje. Ściągawka, dziś ciąg dalszy. Jak wygląda przekazywanie parametrów do funkcji w C++? Jak przekazać wartości do funkcji, aby można było zmodyfikować wartość parametru?

Pierwsze, co przychodzi do głowy to coś takiego – zwykłe przekazanie parametru jako wartość:

Przekazywanie parametrów przez wartość

Co tu się jednak stało?

Wartość zmiennej liczba1 nie zmieniła się. W funkcji Zwieksz(int liczba), liczbajest zmienną lokalną. Po wyjściu z zakresu funkcji, wszystko zostaje zapomniane. Wartość nie zostaje nijak przekazana do liczba1, przekazanej jako parametr.

Oczywiście można było zrobić return liczba, jednak nie o to chodzi: wyobraźmy sobie, że funkcja ma zmodyfikować cały szereg wartości. Dodatkowo takie działanie jest nieoptymalne – w momencie rozpoczęcia działania funkcji zostaje utworzona kopia obiektu.

Wniosek? Nawet proste typy muszą być przekazywane przez wskaźnik albo referencję.

Przekazywanie parametrów przez referencję

Jedyna zmiana w stosunku do poprzedniego przykładu, to dodanie znaczka ampersand w nagłówku funkcji: ZwiekszPrzezReferencje(int &liczba). W środku jednak dzieje się magia i przykład zaczyna działać. Nawet po wyjściu poza zakres funkcji, zmienna liczba1 została zmodyfikowana, dlatego że referencja jest tożsama z obiektem, do którego się odnosi. Zatem &liczba jest dokładnie tym samym, co liczba1. A skoro jest tym samym, to zmieniona w jednym miejscu, zmienia wartość liczba1 globalnie. Dość proste, prawda?

Przekazywanie parametrów przez referencję

Przekazywanie parametrów przez wskaźnik = przekazywanie przez wartość

Poza referencją, jest jeszcze druga opcja – przekazywanie przez wskaźnik. Tak naprawdę jest to przekazywanie przez wartość, czyli takie samo, jak w pierwszym („niedziałającym”) przykładzie. Tutaj jednak będzie inaczej, bo używamy wskaźnika. Podczas przekazywania do funkcji argumentów przez wartość, na początku funkcji tworzona jest lokalna kopia wszystkich parametrów przekazanych do funkcji. Ponieważ jednak jako argument funkcji podajemy wskaźnik, owszem, zostanie on skopiowany do lokalnej zmiennej wskaźnikowej, ale nic nam to nie szkodzi! Jego wartość to nadal poprawny adres pamięci do naszej zmiennej liczba1, co za tym idzie, liczba1  zostanie zmodyfikowana wewnątrz funkcji.:

Przekazywanie przez wartość i wskaźnik

Przekazywanie typów złożonych, własnych klas

Żeby unaocznić sobie fakt kopiowania obiektów, można do tego użyć bardziej skomplikowanego przykładu – napisać swoją klasę, a w niej stworzyć konstruktor kopiujący, który uruchamiając się, wypisze tekst na ekran.

I tak oto, zwykłe przekazanie klasy przez wartość, bez przekazywania jej ze wskaźnikiem. Jak można się domyślić, wewnątrz funkcji powstanie kopia, która zostanie usunięta wraz z wyjściem z funkcji:

Przekazywanie klas przez wartość

Jeszcze raz ten sam przykład z klasami, tylko tym razem przekazywanie ich przez referencję oraz przez wartość (wskaźnik):

Przekazywanie klas przez wartość, referencję i wskaźnik

Widać, że konstruktor kopiujący klasy uruchomił się tylko raz – w „niepoprawnym”, pierwszym przykładzie podanym wyżej. W przypadku przekazywania klasy przez referencję czy wskaźnik, nie uruchomił się konstruktor kopiujący. Operacje odbywają się na obiekcie przekazanym do funkcji z zewnątrz.

Przykład z życia – typ QString

W Qt jest taki typ jak QString, będący reprezentacją ciągu znaków. Taki typ można przekazać na  różne sposoby:

  • foo(QString zmienna) – Przekazanie przez wartość. Wywoła się konstruktor kopiujący obiektu QString, tworząc lokalną kopię wewnątrz funkcji. Zmienna zmodyfikowana w funkcji nie zostanie zmodyfikowana na zewnątrz.
  • foo(QString &zmienna) – Przekazanie przez referencję. Pozwala na modyfikowanie zmiennej wewnątrz funkcji. W Qt w ten sposób najwygodniej przekazywać okna – brak nieoptymalnych kopiowań obiektu (bo jest referencja), zachowana możliwość modyfikowania.
  • foo(QString *zmienna) – Przekazanie przez wskaźnik. Pozwala na modyfikowanie. Skomplikowany temat, szczególnie jeśli dołożymy do tego const. Raczej niepolecany sposób 😉
  • foo(const QString &zmienna) – Przekazanie przez referencję. Przekazana zmienna nie będzie modyfikowana  wewnątrz funkcji (jest const). Najlepszy (najbardziej optymalny) sposób przekazywania QString: brak niepotrzebnych kopiowań itd.

Kod źródłowy do przykładów

Cały kod dostępny jest tutaj: https://bitbucket.org/snippets/d9k/9exyp8. Moja wtyczka do prezentacji kodu na WordPressie nie chce współpracować z niektórymi znakami specjalnymi 😉 Tutaj kod jest bardziej bezpieczny i czytelny. W razie czego, mam nadzieję że zrzuty ekranu są wystarczające.

Przydatny wpis? Postaw mi kawę :)

Dodaj komentarz

avatar
  Subscribe  
Powiadom o