490 likes | 702 Views
Wprowadzenie. PK3. Dr inż. Roman Starosolski Pokój nr 527 ( konsultacje etc.) roman.starosolski @ polsl .pl http://sun. aei .polsl.pl/~rstaros/ S/Pk34 / regulaminy materiały zasoby. Plan wykładów. Wprowadzenie Rozszerzenia nieobiektowe Paradygmat obiektowy, klasa
E N D
PK3 Dr inż. Roman Starosolski Pokój nr 527 (konsultacje etc.) roman.starosolski@polsl.pl http://sun.aei.polsl.pl/~rstaros/S/Pk34/ • regulaminy • materiały • zasoby
Plan wykładów • Wprowadzenie • Rozszerzenia nieobiektowe • Paradygmat obiektowy, klasa • Konstruktor, destruktor • Operatory • static, const i volatile • Dziedziczenie, klasy pochodne • Metody wirtualne • Dziedziczenie wielobazowe • Wzorce • Obsługa wyjątków • Biblioteki
C++ historia • C++ wywodzi się z C (oba nadal rozwijane) • C wywodzi się z BCPL (martwy) • nie było języka A
C++ — C z klasami • narzędzie abstrakcji danych • język programowania obiektowego • ulepszony C
Książki o C++ • Bjarne Stroustrup „Język C++” WNT W-wa • International Standard for Information Systems—Programming Language C+ + (draft), ANSI
Książki o C++ • Stroustrup B.: Projektowanie i rozwój języka C++. WNT, Warszawa • Plauger P.J., Biblioteka standardowa C ++. WNT, Warszawa
Książki o C++ • Lippman S.B., Lajoie J.: Podstawy języka C++. WNT, Warszawa • Tondo C. L., Leung B. P.: Podstawy języka C++. Ćwiczenia i rozwiązania. WNT, Warszawa • Grębosz J.: Symfonia C++. RM, W-wa, wyd. 4. • Grębosz J.: Pasja C++. RM, W-wa, wyd. 2.
Komentarz /* komentarz C, stosowany również w C++, wielowierszowy */ // jednowierszowy komentarz C++ // od „//” do końca wiersza
Komentarz Nadal można używać preprocesora do „wyłączania” większych fragmentów kodu źródłowego #if 0 /* komentarz C, stosowany również w C++, wielowierszowy */ // jednowierszowy komentarz C++ #endif
Komentarz /* do komentarzy wielowierszowychużywaj komentarza z C lub z C++ */ // lecz ich nie mieszaj !!! void foo() // tutaj użyj komentarza z C++ { return; // wygodniejszy i bezpieczniejszy }
Standardowe wejście i wyjście • Nadal możemy używać funkcji C z <stdio.h> • Powinniśmy używać operatorów strumieniowych z C++ (biblioteka <iostream.h>) #include <iostream.h> int a,b,c,d; cout << „input a and b \n” ; cin >> a; cin >> b; cout << „input c and d \n” cin >> c >> d;
Standardowe wejście i wyjście • Strumienie nie są tak podatne na błędy, jak funkcje z <stdio.h> (vide scanf() ) • Strumienie • cin - std. input • cout - std. output • cerr - std. error output • Manipulowanie łańcuchami, określanie precyzji, kontrola stanu, etc. – równie funkcjonalne jak w <stdio.h> • nie należy mieszać operatorów C++ <iostream.h> z funkcjami C z <stdio.h>
Deklaracja jest instrukcją • deklaracja wewnątrz bloku jest instrukcją • może być umieszczona po innych instrukcjach { int i=12; cout << „statement”; int j=i; for (int k=0; k<20; k++) j+=k; }
Deklaracja wewnątrz „for” int i = 42; int a[10]; for (int i = 0; i < 10; i++) a[i] = i; int j = i; // j = 42
Deklaracja wewnątrz „for” for ( forinitstatement; condition; expression ) is equivalent to { forinitstatement while ( condition ) { statement expression ; } }
goto • ograniczenia w stosunku do C: • tylko wewnątrz bloku • skok nie może pominąć inicjalizacji
a: int foo() { b: goto b; int i=7; c: return i; } d: void foofoo() { e: return; } goto
Typy • silna kontrola typów (w porównaniu do C) – aby umożliwić kontrolę błędów • automatyczna konwersja typów – gdy nie prowadzi do niejednoznaczności • gdy to tylko możliwe, konwersja bez utraty precyzji/informacji (nie gwarantowane dla wszystkich konwersji: char/short/int/long/single/float/double/signed/unsigned)
Typy • gdy nie określono typu, przyjmuje się int unsigned u; const a; static i; • zaleca się jawne określanie typu int
Typ void * • nie ma automatycznej konwersji do innych typów wskaźnikowych void *malloc(size_t size); char *str; int *pi; str = (char *) malloc(10); pi = (int *)malloc(100);
Typ void * • nie ma automatycznej konwersji do innych typów wskaźnikowych void free(void *block); free(str); free(pi);
const • nazwane stałe mają zastąpićdefiniowane stałe z C const five = 5; void fooj(const int ci); • const – prawdziwe zmienne, z rozmiarem, adresem, operatorami, ale nie wolno ich modyfikować • ochrona przed błędami programisty • umożliwienie optymalizacji przez kompilator
const a wskaźniki • wskaźnik nastałą: const char *pc=”asdf”(lub: char const *pc=”asdf”) • stały wskaźnik: char * const cp=”asdcf”; • stały wskaźnik nastałą : const char * const cpc=”asdcf”; błędy: pc[1]=‘2’; cp++; cpc[1]=‘b’; cpc--; • wskaźnikowi na stałą można przypisać adres zmiennej, odwrotnie nie
enum enum numbers {ZERO, ONE, FIVE=5, SIX}; • „numbers” to opcjonalna nazwa nowego typu • konwersja automatyczna z enum na int • dlaczego enum nie jest popularne?
Referencje referencja (reference) do typu T: T& • wskaźnik, który wygląda jak zmienna int i=7, // zmienna int & iref=i; // referencja – musi być zainicjalizowana // tj. skojarzona ze zmienną (i) iref++; // teraz i==8
void swap(int &i1, int &i2); int a=2, b=3; swap(a,b); void swap(int &i1, int &i2) { int i3; i3=i1; i1=i2; i2=i3; } i1 i2 i3 a b - - - 2 3 - - - 3 2 2 3 - 2 3 2 3 ? 2 3 2 3 2 2 3 3 3 2 3 3 3 2 2 3 2 Referencje
Referencje – problemy • problem: gdy parametr aktualny jest innego typu niż formalny, lub jest wyrażeniem char c; swap(a+20, c); argumenty będą konwertowane do to const (const int), kompilator oczekuje int (nie const), powinien wygenerować błąd, zazwyczaj wygeneruje tylko ostrzerzeni. wywołanie swap() nie odniesie skutku
Referencje – problemy // int &ir=7; ERROR! 7 to stała const int &ir1=7, // OK &ir2=i+f; // OK /* ir1 i ir2 – referencje do stałych obiektów tymczasowych, żyjących tak długo jak ir1 i ir2 */
Referencje – przykład użyteczny int & min(int &i1, int&i2) { return i1<i2 ? i1 : i2; } int a=10, b=20, c; c=min(a,b); min(a,b)++; //a==11 !
Referencje • mogą wprowadzać w błąd • unikaj funkcji modyfikującej swoje argumenty • używaj referencji, gdy ich rzeczywiście potrzebujesz • dla oszczędzania pamięci • dla oszczędzania czasu • do zwracania obiektów, które będą dalej przetwarzane • używaj stałych referencji (optymalizacja, bezpieczeństwo)
Funkcje – Prototypy C++ wymaga prototypów funkcji!!! (wartość zwracana, nazwa, argumenty) • #include „foo_prototypes.h” • void foo(); • void foo() {};
Funkcje – inline inline int sum(int a, int b) { return a+b; } • zastępstwo makrodefinicji • „inline”to tylko zalecenie dla kompilatora • automatyczne rozwijanie inline funkcji • funkcja rozwinięta inline nie ma adresu
Funkcje przeciążone (overloaded) • wiele funkcji o tej samej nazwie, OK. w C++ int f(int, int); int f(int); int f(long);
Funkcje przeciążone • Adres funkcji przeciążonej – kompilator wymaga przesłanek wyboru wariantu funkcji int (*pd)(long); //OK pd=f; void *pf; // pf=f; // ERROR!
Funkcje przeciążone • Parametryfunkcji przeciążonej muszą się różnić, • Typ zwracany nie służy do rozróżniania funkcji przeciążonych, jest istotny gdy funkcja jest wywoływana int f(int, int); int f(int); int f(long); // OK // int f(int); double f(int); ERROR int ff(int); int ff(double); // OK. // ff(1L) ERROR! niejednoznaczność // void *p=ff(1) ERROR! typ zwracany
Dopasowywanie funkcji przeciążonych • nie więcej niż jedna konwersja każdego argumentu • wybierany najniższy poziom z możliwym dopasowaniem, niejednoznaczność to błąd dopasowania • ścisła zgodność; bez konwersji lub tylko: nazwa tablicy na wskaźnik, nazwa funkcji na wskaźnik, T na const T • zgodność przy promocjach w zakresie typów całkowitych: char na int, short na int, poprzednie dla typów bez znaku, float na double • konwersje standardowe: int ↔ double, pochodna* na podstawowa*, unsigned int ↔ int • konwersje zdef. przez użytkownika • zgodność przy zastosowaniu wielokropka (…)
Dopasowywanie funkcji przeciążonych int f(int); double f(double) void f(); int i1=f(1); // OK int i2=f(1.0); // OK, wywołanie double f(double), // konwersja wyniku int //int i3=f(„Hi”); ERROR! nie ma konwersji // char * do int ani do double
Funkcje przeciążone • wygodne rozszerzenie konwersji automatycznych i jawnych int cmp (double a, double b) { return a – b; } int cmp (char *a, char *b) { return strcmp(a,b); }
Funkcje przeciążone • nadal korzystamy z konwersji automatycznych cmp (1, 2); //OK., konwersja do double przed //wywołaniem cmp (double a, double b) • można zoptymalizować kod int cmp (int a, int b) // teraz cmp(1, 2) bez konwersji { return a – b; }
Domyślne argumenty funkcji void line(int len, char c=‘*’) { for (int i=0; i<len; i++) cout << c; } • teraz to: line(x); znaczy: line(x, ‘*’); • a to: line(x, ‘o’); znaczy: line(x, ‘o’);
Domyślne argumenty funkcji int fun(int i1, int i2=20, int i3=30); • po pierwszym domyślnym wszystkie następne muszą być domyślne • funkcja taka nie jest przeciążona, &fun(int) == &fun(int, int) • możemy niezależnie stosować przeciążanie: int fun(int i1, int i2); //deklaracja nie jest niejednoznaczna, //ale wywołanie fun (int, int) jest!
Domyślne argumenty funkcji • uwaga na „type * =” — po „*” spacja • argumenty domyślne można zdefiniować tylko raz (formalnie albo w prototypie, albo w definicji). Argumenty domyślne definiuj w pliku nagłówkowym!
Zmienna liczba argumentów funkcji • w C pisaliśmy: int printf(char *, …); • w C++ jest prościej: int printf(char * …); // można pominąć przecinek przed …, // ale tylko wtedy,gdy zmienna poprzedzająca „…” // nie ma wartości domyślnej. // ma <stdarg.h > • w C++ jest prościej: funkcje można przeciążać
Zarządzanie pamięcią • przydział pamięci: operator new składnia: new Typ int * pi = new int; // pi = (int*)malloc(sizeof(int)) • zwolnienie: operator delete składnia: delete pointer delete pi;// operator delete nie zmienia wartości // swojego argumentu
Zarządzanie pamięcią • zarządzanie wektorami (tablicami) int * pai = new int [x]; delete [] pai; • programista sam decyduje, którą wersję operatora delete użyć (kompilator tego nie sprawdzi i nie ostrzeże) • wielowymiarowe tablice: int *pmai = new int[x][20][30] // wszystkie wymiary, za wyjątkem pierwszego, // muszą być znane już podczas kompilacji delete [] pmai;
Zarządzanie pamięcią • można zadeklarować funkcję (new_handler), będzie wywołana gdy zabraknie pamięci set_new_handler(); //<new.h> • gdy nie zdefiniowano new_handler, new zwraca 0 (NULL) • delete NULL; nie robi nic • nie mieszaj new/delete z malloc/free
Inne rozszerzenia • Szablony (Templates) • Wyjątki (Exceptions) • Namespace