Wskaźniki i referencje jako parametry

2008-07-09 19:07

Kiedy w C++ chcemy przekazać do funkcji odwołanie do obiektu (zezwalające na jego modyfikację wewnątrz funkcji), mamy do wyboru dwie metody. Ta alternatywa to posłużenie się wskaźnikiem albo referencją:

  1. void Function(Foo* pFoo);
  2. void Function(Foo& foo)

Czy istnieje uniwersalna odpowiedź na to, którą wybrać? Chyba nie. Jeśli chodzi o wskaźnik, to za jego użyciem może przemawiać:

  • Możliwość przekazania odwołania pustego, jeśli parametr nie jest obowiązkowy. Obejmuje to oczywiście zdefiniowanie NULL jako domyślnej wartości dla tego parametru w deklaracji funkcji. Nie jest to możliwe dla referencji (w C++).
  • Fakt, że w wywołaniu funkcji bardziej widoczne jest to, iż przekazany do niej za pośrednictwem wskaźnika obiekt może się zmienić. Jeśli na przykład obiekt ten jest zmienną lokalną, to konieczne jest posłużenie się operatorem &, który daje o tym jakąś widoczną wskazówkę (nie tak jasną jak ref/out w C#, ale zawsze). Nie jestem też wielkim fanem notacji węgierskiej, lecz w przypadku wskaźników stosowanie przedrostka p wydaje mi się akurat wskazane i w tym kontekście też zwiększa czytelność wywołania funkcji, wskazując, że przekazywany obiekt (alokowany na stercie) też może się zmienić.
    1. Foo foo; Foo* pFoo = new Foo();
    2. Function (&Foo); Function (pFoo); // funkcja może zmienić obiekt

Z kolei referencje mogą się popisać innymi zaletami:

  • Nie można do nich przekazać odwołania pustego. To może być zaletą, jeśli taka sytuacja jest niepożądana. Ponadto brak konieczności sprawdzania tego, czy referencja jest “pusta”, może lekko poprawić wydajność kodu generowanego przez kompilator.
  • Składnia użycia obiektu przekazywanego przez referencję zwykle bywa bardziej przejrzysta. Jest tak zwłaszcza wtedy, gdy używamy względem niego operatorów. Na przykład kolekcja dostępna przez wskaźnik musiałaby być indeksowana przez (*pArray)[i], zaś przez referencję po prostu jako array[i].

Widać więc, że jeśli kwestia odwołania pustego nie jest dla nas istotna, to decyzja może być trudna. Ale naturalnie jest tak tylko wtedy, gdy zechcemy się nad takimi sprawami zastanawiać ;]

Tags: ,
Author: Xion, posted under Programming »


6 comments for post “Wskaźniki i referencje jako parametry”.
  1. yomyn:
    July 10th, 2008 o 9:55

    Bardzo ciekawa notka. Ja osobiscie uzywam referencji, a to dlatego ze o niej najpierw sie uczylem, a pozniej trudno mi sie bylo przyzwyczaic do wskaznikow w tej roli :P

  2. RedHot:
    July 10th, 2008 o 12:48

    “. Na przykład tablica dostępna przez wskaźnik musiałaby być indeksowana przez (*pArray)[i]” Albo ja nie rozumiem, albo coś przekręciłeś. Jeśli odbieramy tablicę jako argument w funkcji poprze wskaźnik na nią (dekl. funkcji –
    int f1(int* wsk) ) , to wewnątrz funkcji do każdego z elemntów odwołujemy się w sposób wsk[i]. Zapis taki jak ty mówisz ( (*ptr)[i]) , tyczy się wskaźnika na wskaźnik na tablicę. Możliwe, że chodziło Ci o to od początku, ale kto się bawi w takie rzeczy, jeśli nie potrzebuje ??

  3. lukaszw:
    July 10th, 2008 o 13:39

    Według mnie mogło też chodzić o klasę z przeciążonym operatorem [], ale nie jestem pewien ;)

  4. Xion:
    July 10th, 2008 o 14:10

    Skrót myślowy :) Dla mnie tablica to kolekcja, czyli np. std::vector. W przypadku zwykłych tablic w stylu C jest rzeczywiście tak, jak mówisz.
    Poprawiłem notkę odpowiednio, żeby nie robić nieporozumień.

  5. Reg:
    July 13th, 2008 o 19:10

    Współcześnie stosuje się chyba określenie parametr wejściowy (in), wyjściowy (out), wejściowy-wyjściowy (inout). Tak robi MSDN, tak robi dokumentacja Doxygen i inne dokumentacje. Tak też powinien robić nowoczesny język programowania i C# ma coś takiego. C++ oczywiście nie ma i dlatego ja sobie oznaczam takie parametry odpowiednio w ich nazwie, np. Funkcja(int *OutValue).

  6. Asmodeusz:
    July 17th, 2008 o 9:01

    Jak wspomniał Reg, do oznaczania tego, czy parametr jest wejściowy, wyjściowy czy “IO” jest dokumentacja i komentarze. Sam stosuję niewielkie rozwinięcie XMLowego formatu komentarzy z Visual Studio – m.in. zawierające parametr określający czy jest to wskaźnik wejścia czy wyjścia, pole nazwy klasy (bo C# ma np. wkurzającą cechę zamieniania Int32 na int, czy UInt16 na ushort itd.), informację o ewentualnej zmianie wartości wskazywanej (dla referencji do wskaźników w C++ – prtmode=maychange lub ptrmode=alwayschange).

    Sam podział referencja/wskaźnik stosuję niemal dokładnie tak, jak Xion. Przez wskaźniki przekazuję parametry opcjonalne (chyba że to parametry typu int, float itp. – wtedy wartość nieprawidłowa dla danego wywołania oznacza brak użycia parametru – przykład: w IMesh::DrawInstances(UINT instCount) jeśli instCount wynosi 0, mesh jest renderowany bez uwzględniania instancingu) oraz wszystkie wskaźniki do zewnętrznych interfejsów (typu ID3D10Device, ID3D10Effect itd.). Cała reszta przez wartość, stałą referencję (dla wszystkiego ponad 64 bity) lub referencję (jeśli modyfikowalne w funkcji).

Comments are disabled.
 


© 2019 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.