Transcript Slajd 1
Wprowadzenie
PK3
Dr inż. Roman Starosolski
Pokój nr 527 (konsultacje etc.)
[email protected]
http://sun.aei.polsl.pl/~rstaros/S/Pk34/
regulaminy
materiały
zasoby
Plan wykładów
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
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.
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
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
cout
cerr
- std. input
- std. output
- 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
goto
a:
int foo()
{
void foofoo()
{
e:
b:
return;
goto b;
int i=7;
c:
return i;
}
d:
}
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/u
nsigned)
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 na stałą: const char *pc=”asdf”
(lub: char const *pc=”asdf”)
stały wskaźnik: char * const cp=”asdcf”;
stały wskaźnik na stałą :
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
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
2
3
3
3
3
3
2
?
2
2
2
2
2
3
3
3
3
3
2
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
Parametry funkcji 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
1.
2.
3.
1)
2)
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);
int i2=f(1.0);
//int i3=f(„Hi”);
//
// OK
// OK, wywołanie double f(double),
// konwersja wyniku int
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)
{
return a – b;
}
// teraz cmp(1, 2) bez konwersji
Domyślne argumenty funkcji
void line(int len, char c=‘*’)
{
for (int i=0; i<len; i++)
cout << c;
}
teraz to:
znaczy:
line(x);
line(x, ‘*’);
a to:
znaczy:
line(x, ‘o’);
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