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