Programowanie obiektowe w C++
dr inż. Tadeusz Wilusz
Akademia Ekonomiczna w Krakowie 31-510 Kraków, ul. Rakowicka 27 Budynek Biblioteki Głównej pokój 429 tel.: 2935-264
e-mail: wiluszt@ae.krakow.pl
Programowanie obiektowe
w C++
Wykład 01 Temat wiodący:
Wprowadzenie. Różnice C / C++
Plan wykładu
n
Wprowadzenie
n
Rozszerzenia nieobiektowe
n
Paradygmat obiektowy, klasa
Wprowadzenie
C++ historia
n
C++ wywodzi się z C (oba nadal rozwijane)
n
C wywodzi się z BCPL (martwy)
n
nie było języka A
C++ — C z klasami
n
narzędzie abstrakcji danych
n
język programowania obiektowego
n
ulepszony C
Książki o C++
n
Grębosz J.: Symfonia C++,Kallimach, Kraków
n
Porębski W.: Język C++. Wprowadzenie do programowania,HELP, W-wa
n
Grębosz J.: Pasja C++,Kallimach, Kraków
n
Plauger P.J., Biblioteka standardowa C ++.
WNT, Warszawa
n
Stroustrup B.: Język C++, WNT, W-wa
n
Stroustrup B.: Projektowanie i rozwój języka C++. WNT, Warszawa
Rozszerzenia C / C++
Rozszerzenia nieobiektowe:
•komentarze „do końca linii”
•wygodniejsza biblioteka we/wy;
•funkcje inline;
•argumenty domniemane funkcji;
•referencje (przezwiska);
•nowe operatory new i delete;
•możliwość przeciążania nazw funkcji;
Rozszerzenia obiektowe:
•klasy pozwalające na definiowanie typów obiektów;
•funkcje i dane składowe klas (enkapsulacja);
•możliwość tworzenia nowych klas przez dziedziczenie własności klas bazowych;
•sterowanie ukrywaniem informacji zawartej wewnątrz klasy;
•konstruktory i destruktory obiektów;
Nieobiektowe rozszerzenia C++
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 wielowierszowych
uż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
n Nadal możemy używać funkcji C z <stdio.h>
n 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
n Strumienie nie są tak podatne na błędy, jak funkcje z <stdio.h>
(vide scanf() )
n Strumienie
n cin - std. input
n cout - std. output
n cerr - std. error output
n Manipulowanie łańcuchami, określanie precyzji, kontrola stanu, etc. – równie funkcjonalne jak w <stdio.h>
n nie należy mieszać operatorów C++ <iostream.h> z funkcjami C z <stdio.h>
Deklaracja jest instrukcją
n deklaracja wewnątrz bloku jest instrukcją
n 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
n
ograniczenia w stosunku do C:
n
tylko wewnątrz bloku
n
skok nie może pominąć inicjalizacji
goto
a:
int foo() { b:
goto b;
int i=7;
c:
return i;
} d:
void foofoo() {
e:
return;
}
Typy
n
silna kontrola typów (w porównaniu do C) – aby umożliwić kontrolę błędów
n
automatyczna konwersja typów – gdy nie prowadzi do niejednoznaczności
n
gdy to tylko możliwe, konwersja bez utraty precyzji/informacji (nie gwarantowane dla wszystkich konwersji:
char/short/int/long/single/float/double/signed/u
Typy
n
gdy nie określono typu, przyjmuje się int unsigned u;
const a;
static i;
n
zaleca się jawne określanie typu int
Typ void *
n
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 *
n
nie ma automatycznej konwersji do innych typów wskaźnikowych
void free(void *block);
free(str);
free(pi);
const
n
nazwane stałe mają zastąpić definiowane stałe z C const five = 5;
void fooj(const int ci);
n
const – prawdziwe zmienne, z rozmiarem, adresem, operatorami, ale nie wolno ich modyfikować
n
ochrona przed błędami programisty
umożliwienie optymalizacji przez kompilator
const a wskaźniki
n
wskaźnik na stałą: const char *pc=”asdf”
(lub: char const *pc=”asdf”)
n
stały wskaźnik: char * const cp=”asdcf”;
n
stały wskaźnik na stałą :
const char * const cpc=”asdcf”;
błędy: pc[1]=‘2’; cp++; cpc[1]=‘b’; cpc--;
n
wskaźnikowi na stałą można przypisać adres zmiennej, odwrotnie nie
enum
enum numbers {ZERO, ONE, FIVE=5, SIX};
n
„numbers” to opcjonalna nazwa nowego typu
n
konwersja automatyczna z enum na int
n
dlaczego enum nie jest popularne?
Referencje
referencja (reference) do typu T: T&
n
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
Referencje
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 – problemy
n 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
n
mogą wprowadzać w błąd
n
unikaj funkcji modyfikującej swoje argumenty
n
używaj referencji, gdy ich rzeczywiście potrzebujesz
n dla oszczędzania pamięci
n dla oszczędzania czasu
n do zwracania obiektów, które będą dalej przetwarzane
n
używaj stałych referencji (optymalizacja,
Funkcje – Prototypy
C++ wymaga prototypów funkcji!!!
(wartość zwracana, nazwa, argumenty)
n
#include „foo_prototypes.h”
n
void foo();
n
void foo() {};
Funkcje – inline
inline int sum(int a, int b) {
return a+b;
}
n
zastępstwo makrodefinicji
n
„inline” to tylko zalecenie dla kompilatora
n
automatyczne rozwijanie inline funkcji
n
funkcja rozwinięta inline nie ma adresu
Funkcje przeciążone (overloaded)
n
wiele funkcji o tej samej nazwie, OK. w C++
int f(int, int);
int f(int);
int f(long);
Funkcje przeciążone
n
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
n Parametry funkcji przeciążonej muszą się różnić,
n 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
n nie więcej niż jedna konwersja każdego argumentu
n wybierany najniższy poziom z możliwym dopasowaniem, niejednoznaczność to błąd dopasowania
n ścisła zgodność; bez konwersji lub tylko: nazwa tablicy na wskaźnik, nazwa funkcji na wskaźnik, T na const T
n zgodność przy promocjach w zakresie typów całkowitych:
char na int, short na int, poprzednie dla typów bez znaku, float na double
n konwersje standardowe: int ? double, pochodna* na podstawowa*, unsigned int ? int
n konwersje zdef. przez użytkownika
n 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
n 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
n nadal korzystamy z konwersji automatycznych cmp (1, 2); //OK., konwersja do double przed
//wywołaniem cmp (double a, double b)
n 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;
}
n teraz to: line(x);
znaczy: line(x, ‘*’);
n a to: line(x, ‘o’);
znaczy: line(x, ‘o’);
Domyślne argumenty funkcji
int fun(int i1, int i2=20, int i3=30);
n
po pierwszym domyślnym wszystkie następne muszą być domyślne
n
funkcja taka nie jest przeciążona,
&fun(int) == &fun(int, int)
n
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
n
uwaga na „type * =” — po „*” spacja
n
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
n w C pisaliśmy:
int printf(char *, …);
n 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 >
n w C++ jest prościej:
funkcje można przeciążać
Zarządzanie pamięcią
n przydział pamięci: operator new składnia: new Typ
int * pi = new int; // pi = (int*)malloc(sizeof(int))
n zwolnienie: operator delete składnia: delete pointer
delete pi; // operator delete nie zmienia wartości // swojego argumentu
Zarządzanie pamięcią
n zarządzanie wektorami (tablicami) int * pai = new int [x];
delete [] pai;
n programista sam decyduje, którą wersję operatora delete użyć (kompilator tego nie sprawdzi i nie ostrzeże)
n 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ą
n
można zadeklarować funkcję (new_handler), będzie wywołana gdy zabraknie pamięci
set_new_handler(); //<new.h>
n
gdy nie zdefiniowano new_handler, new zwraca 0 (NULL)
n
delete NULL; nie robi nic
n
nie mieszaj new/delete z malloc/free
Inne rozszerzenia
n
Szablony (Templates)
n
Wyjątki (Exceptions)
n
Namespace
Paradygmat programowania
obiektowego, klasa, obiekt
Paradygmat
paradygmat (słownik PWN) — przyjęty sposób widzenia rzeczywistości w danej dziedzinie
n
Paradygmaty programowania
n
programowanie strukturalne
n
programowanie obiektowe
Paradygmat programowania obiektowego
programowanie obiektowe — to paradygmat rozwiązywania problemów programistycznych
z wykorzystaniem obiektów, sposób interpretacji problemu jako zbioru obiektów i
relacji pomiędzy nimi.
Obiekt
n
Potoczne znaczenie słowa obiekt
n
Znaczenie pojęcia obiektu w programowaniu
n
Reprezentuje na potrzeby programu obiekty ze świata rzeczywistego lub abstrakcyjne (w potocznym znaczeniu słowa obiekt)
n
Uogólniona zmienna (struktura)
n
Zdefiniowany i używany zgodnie ze składnią i semantyką języka
Obiekt - uogólniona zmienna (struktura)
n
Struktura
n
zestaw danych, najczęściej różnych typów
n
Uogólniona
n