ďťż

Ładny brzuch

Czym jest zwrot wartości funkcji poprzez referencję, np.
int & funkcja (int zm1, char znak);
co później można zrobić z tą wartością i czym się różni od zwracania poprzez wartość. Proszę także o króciutki przykładowy kawałek kodu. B)
Szczególnie chodzi mi o rozróżnienie zwracania przez referencję, poprzez wartość i wskźnik.
Bardzo proszę o odpowiedź. Już conajmniej miesiąc straciłem na ten temat i to bez skutku.
Użytkownik comp123 edytował ten post 29 maj 2005, 20:21


Zwracanie referencji zwraca... referencję, czyli adres ;) Referencje to tak na prawdę wskaźniki z bardziej czytelnym zapisem. Jako przykład [wiem, że zawiły], może służyć operator[] w pojemnikach takich jak std::vector, std::basic_string, etc:
int& CNaszPojemnik::operator[](int n);
Po co jest ten '&'? Otóż po to, żeby poniższy zapis miał sens:
naszPojemnik[128] = 16;
Gdyby znaczka & nie było, tj. funkcja zwracałaby wartość, to 16 przypisywalibyśmy do tymczasowej zmiennej, kopii zawartości zmiennej pod naszPojemnik[128]. Jednak zwracając i nadpisując referencję modyfikujemy to, co jest tam naprawdę. Inny przykład - funkcja dostępowa:
class Klasa
{
private:
int zmienna;

public:
inline int& Zmienna(){return zmienna;}

};
Tworzymy obiekt:
Klasa obiekt;
zapis:
obiekt.zmienna = 2;
jest nieprawidłowy, ponieważ zmienna znajduje się w sekcji private klasy Klasa. Jednak można zapisać to tak:
obiekt.Zmienna() = 2;
Gdyby funkcja Zmienna() zwracała samą wartość to znowu ustawialibyśmy zmienną tymczasową. Jednak dzięki temu, że zwraca referencję, operujemy na prawdziwej zmiennej członkowskiej klasy.

Pozdrawiam, TeMPOraL.

Nie wyjaśniono różnicy referencja-wskaźnik.
Referencyja jest tylko nazwą zastępczą jakiejś tam zmiennej, natomiast wskaźnik jest fizycznie istniejącą zmienną, która przechowuje adres innej zmiennej.


...
Gdyby funkcja Zmienna() zwracała samą wartość to znowu ustawialibyśmy zmienną tymczasową. Jednak dzięki temu, że zwraca referencję, operujemy na prawdziwej zmiennej członkowskiej klasy.

Pozdrawiam, TeMPOraL.



jak już to rekursja :)

ze znaczkiem & czyli rekursja zmienia wartość i nadaje nową
bez znaczka & nie zmienia

jasniej

mam

x=5
y=3

void funkcja (&x,&y)
{
x=x+2;
y=y+4;
}

spowoduje ze w zmeinnych beda nastepujace wartosci

x=7 // a nie x=5
y=7 // a nie y=3

natomiast bez rekursji
funkcja (x,y)
{
x=x+2;
y=y+4;
}
nie powoduje zmiany wartosci nadal beda

x=5
y=3

zmienione beda jedynie w ciele funkcji :)

lol :DDD a to mnie teraz rozwaliles :D rekursja :D poczytaj np na wikipedii co to jest rekursja (rekurencja) :D

Zwrócenie referencji przez funkcje operatora << umożliwia wypisywanie "kaskadowe"
ostream & operator<<(ostream & str, twoja_klasa & abc) {  str << abc.skladnik1 << abc.skladnik2;  return str; }
I teraz mozesz sobie wypisawac np tak:
twoja_klasa agd; twoja_klasa wrt; cout << agd << "Joł cziki czoł! " << wrt << endl; // Jakbys nie mial tego operatora to musialbys: cout << agd; cout << "Joł cziki czoł! "; cout << wrt; cout << endl;
Przydatne :D

WTF?
Co wy tu znowu odstawiacie? N/C.
Rekursja to jest tak samo inteligentne pojęcie, jak infameous "przeciągnij i upuść". Ja tam nie jestem profesorem M., ale w języku polskim przyjęło się używać słowa 'rekurencja'.

A już kompletnie nie wiem co to ma do referencji - referencja, czyli ODNIESIENIE. ODNOSIĆ SIĘ do czegoś. Referencja to taka 'naklejka' na nazwę, i w żaden sposób nie powinna być kojarzona z rekurencją.

Co do referencji i wskaźników:
void funkcja(int* ptr);
jest w praktyce równoznaczne temu:
void funkcja(int& ref);
tylko w kodzie funkcji w pierwszym przypadku możemy napisać:
*ptr = 128;
a w drugim:
ref = 128;
w tym kontekscie referencje służą tylko ułatwieniu zapisu. Piszę 'w tym kontekscie', ponieważ na referencjach nie można wykonywać działań arytmetycznych takich, jak na wskaźnikach. Powód jest prosty - referencja jest zawsze przyklejona do konkretnego obiektu i musi być od razu zainicjalizowana. Nie ma praktycznie sposobu na to, by referencja wskazywała na nic.

(tzn. tak na prawdę jest, oto on:
int* ptr = NULL;
int& ref = *ptr;
ale tak piszących programistów powinno się wieszać na drzewach.)

Proszę jeszcze o wytłumaczenie mi różnicy pomiędzy zwracaniem wartości, a referencji przez funkcję. Jak dotad rozumiem tylko dlaczego przekazujemy argumenty w postaci referencji(aby możliwe było operowanie na ich wartościach spod oryginalnych adresów). Jeśli można byłoby o zwięzły, prosty i rzeczowy przykład to bardzo dziękuję. I jeszcze jedno: programik zwykły, bez klas. B)
Użytkownik comp123 edytował ten post 30 maj 2005, 18:04
Będę bardzo, ale to bardzo konkretny - zacytuję Ci fragment z Megatutoriala [Xion, nie bij! Warsztacie, nie odrzucajcie mnie za ten czyn!]. Bardziej łopatologicznie się już nie da [tj. Symfonia niekiedy bywa bardziej] - najlepiej weź zacznij się uczyć C++ z MegaTutoriala - www.avocado.risp.pl


Referencje i funkcje

Chyba jedynym miejscem, gdzie rzeczywiście używa się referencji, są nagłówki funkcji (prototypy). Dotyczy to zarówno parametrów, jak i wartości przez te funkcje zwracanych. Referencje dają bowiem całkiem znaczące optymalizacje w szybkości działania kodu, i to w zasadzie za darmo. Nie wymagają żadnego dodatkowego wysiłku poza ich użyciem w miejsce zwykłych typów.

Brzmi to bardzo kusząco, zatem zobaczmy te wyśmienite rozwiązania w akcji.

Parametry przekazywane przez referencje

Już przy okazji wskaźników zauważyliśmy, że wykorzystanie ich jako parametrów funkcji może przyspieszyć działanie programu. Zamiast całych obiektów funkcja otrzymuje wtedy odwołania do nich, zaś poprzez nie może odnosić się do faktycznych obiektów. Na potrzeby funkcji kopiowane są więc tylko 4 bajty odwołania, a nie czasem wiele kilobajtów właściwego obiektu!

Przy tej samej okazji narzekaliśmy jednak, że zastosowanie wskaźników wymaga przeformatowania składni całego kodu, w którym należy dodać konieczne dereferencje i zmienić operatory wyłuskania. To niewielki, ale jednak dolegliwy kłopot.

I oto nagle pojawia się cudowne rozwiązanie :) Referencje, bo o nich rzecz jasna mówimy, są także odwołaniami do obiektów, ale możliwe jest stosowanie wobec nich zwyczajnej składni, bez uciążliwości związanych ze wskaźnikami. Czyniąc je parametrami funkcji, powinniśmy więc upiec dwie pieczenie na jednym ogniu, poprawiając zarówno osiągi programu, jak i własne samopoczucie :D

Spójrzmy zatem jeszcze raz na funkcję Wyszukaj(), z którą spotkaliśmy się już przy wskaźnikach. Tym razem jej parametry będą jednak referencjami. Oto jak wpłynie to na wygląd kodu:

#include <string>
int Wyszukaj  (const std::string& strSzukany,
              const std::string& strPrzeszukiwany)
{
  // przeszukujemy nasz napis
  for (unsigned i = 0;
        i <= strPrzeszukiwany.length() - strSzukany.length(); ++i)
  {
        // porównujemy kolejne wycinki napisu
        if (strPrzeszukiwany.substr(i, strSzukany.length())
            == strSzukany)
              // jeżeli wycinek zgadza się, to zwracamy jego indeks
              return i;
  }

  // w razie niepowodzenia zwracamy -1
  return -1;
}

Obecnie nie widać tu najmniejszych oznak silenia się na jakąkolwiek optymalizację, a mimo jest ona taka sama jak w wersji wskaźnikowej. Powodem jest forma nagłówka funkcji:

int Wyszukaj  (const std::string& strSzukany,
              const std::string& strPrzeszukiwany)

Oba jej parametry są tutaj referencjami do stałych napisów, a więc nie są kopiowane w inne miejsca pamięci wyłącznie na potrzeby funkcji. A jednak, chociaż faktycznie funkcja otrzymuje tylko ich adresy, możemy operować na tych parametrach zupełnie tak samo, jakbyśmy dostali całe obiekty poprzez ich wartości. Mamy więc zarówno wygodną składnię, jak i dobrą wydajność tak napisanej funkcji.

Zatrzymajmy się jeszcze przez chwilę przy modyfikatorach const w obu parametrach funkcji. Obydwa napisy nie w jej ciele w żaden sposób zmieniane (bo i nie powinny), zatem logiczne jest zadeklarowanie ich jako referencji do stałych. W praktyce tylko takie referencje stosuje się jako parametry funkcji; jeżeli bowiem należy zwrócić jakąś wartość poprzez parametr, wtedy lepiej dla zaznaczenia tego faktu użyć odpowiedniego wskaźnika.

Zwracanie referencji

Na podobnej zasadzie, na jakiej funkcje mogą pobierać referencje poprzez swoje parametry, mogą też je zwracać na zewnątrz. Uzasadnienie dla tego zjawiska jest również takie samo, czyli zaoszczędzenie niepotrzebnego kopiowania wartości.

Najprotszym przykładem może być ciekawe rozwiązanie problemu metod dostępowych - tak jak poniżej:

class CFoo
{
  private:
        unsigned m_uPole;
  public:
        unsigned& Pole()  { return m_uPole; }
};

Ponieważ metoda Pole() zwraca referencję, możemy używać jej niemal tak samo, jak zwyczajnej zmiennej:

CFoo Foo;
Foo.Pole() = 10;
std::cout << Foo.Pole();

Oczywiście kwestia, czy takie rozwiązanie jest w danym przypadku pożądane, jest mocno indywidualna. Zawsze należy rozważyć, czy nie lepiej zastosować tradycyjnego wariantu metod dostępowych - szczególnie, jeżeli chcemy zachowywać kontrolę nad wartościami przypisywanymi polom.

Z praktycznego punktu widzenia zwracanie referencji nie jest więc zbytnio przydatną możliwością. Wspominam jednak o niej, gdyż stanie się ona niezbędna przy okazji przeładowywania operatorów - zagadnienia, którym zajmiemy się w jednym z przyszłych rozdziałów.


EDIT: Co to znaczy 'programik zwykły, bez klas'? Programujemy w C++ czy nie? Trochę ciężko jest znaleść przykład zwracania czegoś przez referencję co nie jest związane z klasą - nie możesz zwrócić obiektu lokalnego funkcji przez referencję, bo obiekt ten zostanie zniszczony pod koniec funkcji a referencja będzie wskazywać na nieprawidłowe miejsce w pamięci. Jak już mówiłem - weź zacznij czytać Megatutka xD

Pozdrawiam.
Użytkownik TeMPOraL edytował ten post 30 maj 2005, 18:15
OK. Proszę o przykład z klasami, ale wreszcie inny od tego typu:
Klasa jakas;
jakas.obiekt() = 2;
lecz inny.
Czytałem już cały rozdział o wskaźnikach i referencjach z C++ Dla każdego lecz nie zrozumiałem zwracania właśnie referencji, oprócz faktu, że ich stosowanie pozwala przyspieszyć wykonywania programu, ponieważ nie wywołujemy konstruktora kopiującego, a zwracamy właściwy adres. B)

Trudno tu wymyśleć jakikolwiek inny przykład - w zwracaniu funkcji przez referencję chodzi tylko o to, że dostajemy referencję do konkretnej, zwracanej zmiennej, a nie kopię tej zmiennej. Dzięki temu możemy wykonywać na niej operacje, i wiemy że tyczą się one tej konkretnej zmiennej, a nie jej kopii.

Niemniej jeśli przypiszemy zwracaną wartosć do jakiejś normalniej zmiennej, np.
zmienna = Klasa.PobierzSkladnik();
to ta zmienna będzie już zawierać jej kopię.

EDIT
Tak, wiem że jest to lamerski zwyczaj, ale właśnie nabiłem sobie 2^8-go posta, a że jest to okrągła liczba, to się cieszę XD.

Pamiętajcie wszyscy: Don't Post Crap.
Użytkownik TeMPOraL edytował ten post 30 maj 2005, 19:12
A czy mógłby ktoś mi wytłumaczyć uzycie zwracania referencji zamiast przezwartość w poniższym programie( z książki C++ Dla każdego, Jesse Liberty):

#include <iostream>

using namespace std;
class SimpleCat
{
public:
SimpleCat();
SimpleCat(SimpleCat&);
~SimpleCat();

int GetAge() const { return itsAge; }
void SetAge(int age) { itsAge = age; }

private:
int itsAge;
};

SimpleCat::SimpleCat()
{
cout << "Konstruktor klasy SimpleCat...\n";
itsAge = 1;
}

SimpleCat::SimpleCat(SimpleCat&)
{
cout << "Konstruktor kopii klasy SimpleCat...\n";
}

SimpleCat::~SimpleCat()
{
cout << "Destruktor klasy SimpleCat...\n";
}

const SimpleCat & FunctionTwo (const SimpleCat & theCat);

int main()
{
cout << "Tworze obiekt...\n";
SimpleCat Mruczek;
cout << "Mruczek ma " ;
cout << Mruczek.GetAge();
cout << " lat\n";
int age = 5;
Mruczek.SetAge(age);
cout << "Mruczek ma " ;
cout << Mruczek.GetAge();
cout << " lat\n";
cout << "Wywoluje funkcje FunctionTwo...\n";
FunctionTwo(Mruczek);
cout << "Mruczek ma " ;
cout << Mruczek.GetAge();
cout << " lat\n";
return 0;
}

// functionTwo, otrzymuje referencję do obiektu const
const SimpleCat & FunctionTwo (const SimpleCat & theCat)
{
cout << "FunctionTwo. Wracam...\n";
cout << "Mruczek ma teraz " << theCat.GetAge();
cout << " lat \n";
// theCat.SetAge(8); const!
return theCat; // dlaczego poprzez referencję
}

i jeszcze jedno pytanie: czy zamiast tej referencji mógłbym użyć wskaźników?
Uprzejmie proszę o odpowiedź.
Użytkownik comp123 edytował ten post 30 maj 2005, 21:07

...


W tym kodzie nie widzę sensu zastosowania referencji. Może autor kursu chciał tego kodu użyć w następnym rozdziale czy coś?


Najprawdopodobniej chodziło tylko o pokazanie, jak to sie robi przez referencje. w FunctionTwo zwracanie przez refernecje est bez sensu, zresztom cała funkcja jast bez sensu. Chodzi o zapis

przy referencjach jest jeszcze jeden myk optymalizacyjny w stosunku do wskaznikow o czym Xion nie wspomnial (a przynajmniej ja nie widzialem). Przekazujac jako referencje kompilator wie dokladnie do jakiego obiektu/zmiennej odnosi sie dane wywolanie. Ma to potezne znaczenie w przypatku funkcji inline i bardziej kompleksowych rzeczy - np przekazywania obiektow kontenerow stl jako referencje.

Przy wskaznikach komplilator ma strasznie trudne zadanie na okreslenie zrodla i podjecia extra optymalizacji - przy referencjach wie dokladnie do czego sie odnosi i czesto-gesto wykorzystuje ten fakt do efektywniejszej wymiany informacji miedzy funkcja a reszta kodu.

No i przy referencjach nie istnieje grozba przekazania NULL-a - tzn teoretycznie mozna ale to dosyc trudne - anyway - kod staje sie bardziej bezpieczny/odporny na glupie bledy :).

//EDIT zeby bylo jasne - mowilem o przesylaniu referencji DO funkcji, a nie zwracaniu - uzywanie zwracania (IMO) poza operatorami jest pozbawione sensu

pozdr.
st3tc
Użytkownik st3tc edytował ten post 02 czerwiec 2005, 11:22
Jeśli w poprzednim przykładzie zwracanie referencji nie miało głębszego sensu, w takim razie jaki sens ma w poniższym (C++ Dla każdego, Jesse Liberty)?:
(gdy zastąpiłem zwracanie referencji zwracaniem przez wartość kod nadal działał)

#include <iostream>

using namespace std;

class Counter
{
public:
Counter();
~Counter(){}
int GetItsVal()const { return itsVal; }
void SetItsVal(int x) {itsVal = x; }
void Increment() { ++itsVal; }
const Counter& operator++ ();

private:
int itsVal;

};

Counter::Counter():
itsVal(0)
{};

const Counter& Counter::operator++()
{
++itsVal;
return *this;
}

int main()
{
Counter i;
cout << "Wartoscia i jest " << i.GetItsVal() << endl;
i.Increment();
cout << "Wartoscia i jest " << i.GetItsVal() << endl;
++i;
cout << "Wartoscia i jest " << i.GetItsVal() << endl;
Counter a = ++i;
cout << "Wartoscia a jest: " << a.GetItsVal();
cout << ", zas wartosc i to: " << i.GetItsVal() << endl;
return 0;
}


Jeśli w poprzednim przykładzie zwracanie referencji nie miało głębszego sensu, w takim razie jaki sens ma w poniższym (C++ Dla każdego, Jesse Liberty)?: (gdy zastąpiłem zwracanie referencji zwracaniem przez wartość kod nadal działał)

Pomysl troszke - to nie boli. Albo wez w koncu zerknij w jakies doce czy nawet tego calego megatutka - a nie zadajesz takie pytania :/.

Postaraj sie pomyslec co sie dzieje w programie gdy operator zwraca referencje do "siebie" a co gdy zwroci przez wartosc. I pomysl jeszcze co sie tak naprawde dzieje przy zwracaniu przez wartosc. Bedziesz wiedzial czemu uzywa sie tu "&" i dlaczego to jest efektywniejsze

I prosze po mnie nie jechac a tylko wysilic mozgownice :)

//EDIT:
Kurde powinni Cie spalic na stosie patrz co sam napisales - i jeszcze zadajesz takie pytania ! :

Co by Ci bylo latwiej przeczytac wlasnego posta - zboldowalem Ci fragment


Czytałem już cały rozdział o wskaźnikach i referencjach z C++ Dla każdego lecz nie zrozumiałem zwracania właśnie referencji, oprócz faktu, że ich stosowanie pozwala przyspieszyć wykonywania programu, ponieważ nie wywołujemy konstruktora kopiującego, a zwracamy właściwy adres.

... ... no comments ... ...

A - i nie zadawaj pytabia co to konstruktor kopiujacy bo normalnie nie wiem co zrobie :P
Użytkownik st3tc edytował ten post 02 czerwiec 2005, 13:50
zaplujesz sobie monitor st3tc :D ale racje masz, czasem warto wysilic wlasne szare komorki. panie comp123, przeczytaj moze jeszcze raz ten rozdzial i wszystko bedzie jasne :)

  • zanotowane.pl
  • doc.pisz.pl
  • pdf.pisz.pl
  • zsf.htw.pl
  •