Wykład 2
Transkrypt
Wykład 2
Programowanie obiektowe w C++ Wykład 2 dr Lidia Stępień Akademia im. Jana Długosza w Częstochowie L. Stępień (AJD) POwCPP 1 / 17 Referencje jako parametry funkcji Najczęstszym zastosowaniem referencji jest wykorzystanie jej jako parametrów funkcji - przekazanie przez referencję. W tej roli nazwa zmiennej referencyjnej widoczna w funkcji jest aliasem zmiennej programu wywołującego. Wywołanie funkcji oznacza zainicjalizowanie parametrów funkcji wartościami argumentów wywołania funkcji. Przyspieszenie wykonywania programu dzięki przekazaniu referencji zamiast całego obiektu danych (szczególnie ważne jest to w przypadku dużych obiektów danych, jak struktury i egzemplarze klas. L. Stępień (AJD) POwCPP 2 / 17 // Przykład 1 #include<iostream> using namespace std; // void Zamien(int, int); void Zamien(int*, int*); void Zamien(int&, int&); // #1 // #2 // #3 int main(){ int a = 2, b = 5; cout << a << ", " << b << endl; Zamien(a,b); cout << a << ", " << b << endl; Zamien(&a,&b); cout << a << ", " << b << endl; } void Zamien(int x, int y){ int z = x; x = y; y = z; } void Zamien(int* x, int* y){ int z = *x; *x = *y; *y = z; } void Zamien(int &x, int &y){ int z = x; x = y; y = z; } L. Stępień (AJD) POwCPP 3 / 17 // Przykład 2 #include<iostream> using namespace std; int Szescian(int); // #1 int rSzescian(int&); // #2 int main(){ int a = 2; cout << a << " ^ 3 = cout << a << " ^ 3 = cout << a << " ^ 3 = } " << Szescian(a) << endl; " << rSzescian(a) << endl; " << Szescian(a) << endl; int Szescian(int x){ x *= x * x; return x; } int rSzescian(int rx){ rx *= rx * rx; return rx; } L. Stępień (AJD) POwCPP 4 / 17 UWAGI Jeśli chcemy, aby funkcja nie zmieniała wartości przekazanych jej przez refernecję, należy skorzystać z referencji stałej. int rSzescian(const int& rx); Przy próbie zmiany wartości zmiennej rx, kompilator zgłosi błąd. Kompilator zgłosi błąd przy próbie wywołania funkcji mającej parametry referencyjne dla paramertu nie l-wartości. Jeśli parametr wywołania funkcji nie jest l-wartością, albo nie jest zgodny co do typu do odpowiedniego stałego parametru referencyjnego, C++ tworzy zmienną anonimową właściwego typu i przypisuje jej wartość przekazanego parametru. Od tej chwili parametr odnosi się do takiej zmiennej anonimowej. Kiedy tylko jest możliwe, należy używać specyfikatora const. L. Stępień (AJD) POwCPP 5 / 17 Powody deklarowania parametrów referencyjnych jako stałych: Użycie const chroni przed niezamierzoną zmianą wartości. Użycie const pozwala funkcji przetwarzać wartości stałe i niestałe pominięcie const spowoduje, że funkcja będzie tylko przetwarzać tylko dane niebędące stałymi. Użycie stałej referencji pozwala funkcji generować tymczasowe zmienne i ich używać. L. Stępień (AJD) POwCPP 6 / 17 Zwracanie wartości przez funkcję Gdy funkcja zwraca zwyczajną wartość (jak przekazywanie parametru przez wartość) dochodzi do obliczenia wartości wyrażenia instrukcji return i przekazania obliczonej wartości do miejsca wywołania funkcji. Koncepcyjnie dochodzi wówczas do skopiowania wartości zwracanej do tymczasowej lokalizacji, w której jest dostępna dla programu wywołującego, np. double wynik = sqrt(16.0); cout << sqrt(25.0) << endl; L. Stępień (AJD) POwCPP 7 / 17 Zwracanie referencji przez funkcję Funkcja zwracająca referencję jest tak naprawdę aliasem zmiennej, której referencja dotyczy. double wynik = sqrt(16.0); cout << sqrt(25.0) << endl; int& F(const int &x){ x+=x*x; return x; } int x = 2; int w = F(x); W przypadku zwracania przez funkcję referencji, mamy bezpośrednie kopiowanie zwracanej wartości x do w . L. Stępień (AJD) POwCPP 8 / 17 UWAGI Należy unikać zwracania referencji do zmiennej tymczasowej, która przestanie istnieć w chwili zakończenia wykonywania funkcji. (Analogicznie dla wskaźników.) Najprostszym rozwiązaniem jest zwracanie referencji przekazanej wcześniej jako parametr funkcji. Innym rozwiązaniem jest alokacja pamięci operatorem new. L. Stępień (AJD) POwCPP 9 / 17 Kiedy używać referencji, kiedy wskaźników, a kiedy przekazywać przez wartość? Funkcja korzysta z przekazanych danych (parametrów), ale ich nie modyfikuje: Jeśli obiekt jest mały, np. jest to tym wbudowany lub mała struktura, należy go przekazać przez wartość. Jeśli obiekt danych jest tablicą - musimy użyć wskaźnika. Wskaźnik powinien być typu const. Jeśli obiekt danych jest duży, należy użyć wskaźnika const lub referencji const, by zwiększyć szybkość działania programu. Jeśli obiekt danych jest egzemplarzem klasy, należy użyć referencji const. W C++ to właśnie dla klas stworzono referencję. L. Stępień (AJD) POwCPP 10 / 17 Kiedy używać referencji, kiedy wskaźników, a kiedy przekazywać przez wartość? Funkcja korzysta z przekazanych danych (parametrów), ale ich nie modyfikuje: Jeśli obiekt jest mały, np. jest to tym wbudowany lub mała struktura, należy go przekazać przez wartość. Jeśli obiekt danych jest tablicą - musimy użyć wskaźnika. Wskaźnik powinien być typu const. Jeśli obiekt danych jest duży, należy użyć wskaźnika const lub referencji const, by zwiększyć szybkość działania programu. Jeśli obiekt danych jest egzemplarzem klasy, należy użyć referencji const. W C++ to właśnie dla klas stworzono referencję. L. Stępień (AJD) POwCPP 10 / 17 Kiedy używać referencji, kiedy wskaźników, a kiedy przekazywać przez wartość? Funkcja korzysta z przekazanych danych (parametrów), ale ich nie modyfikuje: Jeśli obiekt jest mały, np. jest to tym wbudowany lub mała struktura, należy go przekazać przez wartość. Jeśli obiekt danych jest tablicą - musimy użyć wskaźnika. Wskaźnik powinien być typu const. Jeśli obiekt danych jest duży, należy użyć wskaźnika const lub referencji const, by zwiększyć szybkość działania programu. Jeśli obiekt danych jest egzemplarzem klasy, należy użyć referencji const. W C++ to właśnie dla klas stworzono referencję. L. Stępień (AJD) POwCPP 10 / 17 Kiedy używać referencji, kiedy wskaźników, a kiedy przekazywać przez wartość? Funkcja korzysta z przekazanych danych (parametrów), ale ich nie modyfikuje: Jeśli obiekt jest mały, np. jest to tym wbudowany lub mała struktura, należy go przekazać przez wartość. Jeśli obiekt danych jest tablicą - musimy użyć wskaźnika. Wskaźnik powinien być typu const. Jeśli obiekt danych jest duży, należy użyć wskaźnika const lub referencji const, by zwiększyć szybkość działania programu. Jeśli obiekt danych jest egzemplarzem klasy, należy użyć referencji const. W C++ to właśnie dla klas stworzono referencję. L. Stępień (AJD) POwCPP 10 / 17 Kiedy używać referencji, kiedy wskaźników, a kiedy przekazywać przez wartość? Funkcja korzysta z przekazanych danych (parametrów), ale ich nie modyfikuje: Jeśli obiekt jest mały, np. jest to tym wbudowany lub mała struktura, należy go przekazać przez wartość. Jeśli obiekt danych jest tablicą - musimy użyć wskaźnika. Wskaźnik powinien być typu const. Jeśli obiekt danych jest duży, należy użyć wskaźnika const lub referencji const, by zwiększyć szybkość działania programu. Jeśli obiekt danych jest egzemplarzem klasy, należy użyć referencji const. W C++ to właśnie dla klas stworzono referencję. L. Stępień (AJD) POwCPP 10 / 17 Kiedy używać referencji, kiedy wskaźników, a kiedy przekazywać przez wartość? Funkcja modyfikuje przekazane dane: Jeśli mamy do czynienia z typem wbudowanym, należy użyć wskaźnika. Wywołanie takiej funkcji jest jednoznaczne. Jeśli obiekt danych jest tablicą - musimy użyć wskaźnika. Jeśli obiekt danych jest strukturą używamy wskaźnika lub referencji. Jeśli obiekt danych jest egzemplarzem klasy, używamy referencji. Są to tylko wytyczne, czasami konieczne jest dokonanie innego wyboru niż wcześniej sugerowane :) L. Stępień (AJD) POwCPP 11 / 17 Kiedy używać referencji, kiedy wskaźników, a kiedy przekazywać przez wartość? Funkcja modyfikuje przekazane dane: Jeśli mamy do czynienia z typem wbudowanym, należy użyć wskaźnika. Wywołanie takiej funkcji jest jednoznaczne. Jeśli obiekt danych jest tablicą - musimy użyć wskaźnika. Jeśli obiekt danych jest strukturą używamy wskaźnika lub referencji. Jeśli obiekt danych jest egzemplarzem klasy, używamy referencji. Są to tylko wytyczne, czasami konieczne jest dokonanie innego wyboru niż wcześniej sugerowane :) L. Stępień (AJD) POwCPP 11 / 17 Kiedy używać referencji, kiedy wskaźników, a kiedy przekazywać przez wartość? Funkcja modyfikuje przekazane dane: Jeśli mamy do czynienia z typem wbudowanym, należy użyć wskaźnika. Wywołanie takiej funkcji jest jednoznaczne. Jeśli obiekt danych jest tablicą - musimy użyć wskaźnika. Jeśli obiekt danych jest strukturą używamy wskaźnika lub referencji. Jeśli obiekt danych jest egzemplarzem klasy, używamy referencji. Są to tylko wytyczne, czasami konieczne jest dokonanie innego wyboru niż wcześniej sugerowane :) L. Stępień (AJD) POwCPP 11 / 17 Kiedy używać referencji, kiedy wskaźników, a kiedy przekazywać przez wartość? Funkcja modyfikuje przekazane dane: Jeśli mamy do czynienia z typem wbudowanym, należy użyć wskaźnika. Wywołanie takiej funkcji jest jednoznaczne. Jeśli obiekt danych jest tablicą - musimy użyć wskaźnika. Jeśli obiekt danych jest strukturą używamy wskaźnika lub referencji. Jeśli obiekt danych jest egzemplarzem klasy, używamy referencji. Są to tylko wytyczne, czasami konieczne jest dokonanie innego wyboru niż wcześniej sugerowane :) L. Stępień (AJD) POwCPP 11 / 17 Kiedy używać referencji, kiedy wskaźników, a kiedy przekazywać przez wartość? Funkcja modyfikuje przekazane dane: Jeśli mamy do czynienia z typem wbudowanym, należy użyć wskaźnika. Wywołanie takiej funkcji jest jednoznaczne. Jeśli obiekt danych jest tablicą - musimy użyć wskaźnika. Jeśli obiekt danych jest strukturą używamy wskaźnika lub referencji. Jeśli obiekt danych jest egzemplarzem klasy, używamy referencji. Są to tylko wytyczne, czasami konieczne jest dokonanie innego wyboru niż wcześniej sugerowane :) L. Stępień (AJD) POwCPP 11 / 17 Kiedy używać referencji, kiedy wskaźników, a kiedy przekazywać przez wartość? Funkcja modyfikuje przekazane dane: Jeśli mamy do czynienia z typem wbudowanym, należy użyć wskaźnika. Wywołanie takiej funkcji jest jednoznaczne. Jeśli obiekt danych jest tablicą - musimy użyć wskaźnika. Jeśli obiekt danych jest strukturą używamy wskaźnika lub referencji. Jeśli obiekt danych jest egzemplarzem klasy, używamy referencji. Są to tylko wytyczne, czasami konieczne jest dokonanie innego wyboru niż wcześniej sugerowane :) L. Stępień (AJD) POwCPP 11 / 17 Klasa biblioteczna string Napis w stylu języka C++ to obiekt typu string: string s("witam"); Przechowywany w obiekcie napis jest ciągiem znaków o zmiennej długości. Korzystanie z typu string wymaga dołączenia pliku nagłówkowego: #include <string> i przestrzeni nazw std. L. Stępień (AJD) POwCPP 12 / 17 Inicjalizowanie obiektów klasy string string s1; // tworzy napis pusty string s2("witam"); // napis z wartością początkową string s3="od tego zaczynamy"; // to samo co wyżej string s4(s3); // można inicjować jeden napis drugim string s7(10,’\n’); // napis ma 10 znaków nowego wiersza L. Stępień (AJD) POwCPP 13 / 17 Operacje na obiektach klasy string Obliczenie długości napisu - metoda length()lub size(): cout << "Napis ma " << s2.length() << " znaków\n"; cout << "Napis ma " << s2.size() << " znaków\n"; Przypisywanie napisów: string s1, s2; s1 = "w klasie string tak można"; s2 = s1; L. Stępień (AJD) POwCPP 14 / 17 Operacje na obiektach klasy string Zachowany jest dostęp do pojedynczych znaków (pozycje napisu są numerowane od 0 do length()-1): string s("wsisiz.edu.pl"); int ile = s.length(); for (int i = 0; i < ile; ++i) if (s[i] == ’.’) s[i] = ’_’; Sprawdzenie, czy napis jest pusty: // za pomocą sprawdzenia długości napisu if (!s2.size()) ... lub // empty() zwraca true, jeśli napis nie ma znaków if (s2.empty()) ... L. Stępień (AJD) POwCPP 15 / 17 Operacje na obiektach klasy string Łączenie napisów (konkatenacja) jest realizowane za pomocą operatora ’+’: string s1("Adam "), s2("Kowalski "), s3; s3=s1+s2; // w s3 jest napis "Adam Kowalski " Do napisu można dołączać znak: string s1("Adam"),s2("Kowalski"),s3; s3=s1+’ ’+s2; // w s3 jest napis "Adam Kowalski" Napisy można porównywać z napisami: if (s2 == s3) ... // operator == jest przeciążony Można używać operatorów: ==, !=, >, <, >= i <= L. Stępień (AJD) POwCPP 16 / 17 Operacje na obiektach klasy string Napisy można wyświetlać tak, jak typy podstawowe (przeciążony operator ’«’): string s("Adam"); cout << s << endl; Napisy można wczytywać (przeciążony operator ’»’ oraz funkcja getline()): string s1, s2; cin >> s1; // do znaku spacji getline(cin,s2); // do znaku nowej linii: jest on wczytywany, // ale nie zachowywany getline(cin,s2,’\n’); // do znaku nowej linii: // jest on wczytywany, ale nie zachowywany, // ogranicznik tekstu można zmienić na inny L. Stępień (AJD) POwCPP 17 / 17