• Nie Znaleziono Wyników

Wstęp do programowania obiektowego

N/A
N/A
Protected

Academic year: 2021

Share "Wstęp do programowania obiektowego"

Copied!
39
0
0

Pełen tekst

(1)

Wstęp do programowania obiektowego

KLASA ISTREAM

KLASA OSTREAM

MANIPULATORY STRUMIENIOWE

STRUKTURY W C++

DOMYŚLNE WARTOŚCI PARAMETRÓW

KONSTRUKTORY I DESTRUKTORY KLAS POCHODNYCH

KONSTRUKTOR KOPIUJĄCY

POLIMORFIZM

(2)

KLASA ISTREAM

2

(3)

Obiekty klasy istream mogą czytać i interpretować sekwencję znaków.

Z tej klasy dziedziczą:

iostream ( strumień uniwersalny),

ifstream (wejście z pliku)

istringstream (wejście z napisu).

(4)

Wybrane metody klasy istream

int get(); // czyta pojedynczy znak

int peek(); // podgląd następnego znaku

istream& putback(char c); // zwraca znak do strum.

istream& ignore(int n=1); // opuszcza n znaków

istream& getline (char* s, streamsize n );

istream& getline (char* s, streamsize n, char delim );

// czyta znaki ze strumienia (maksymalnie n) dopóki nie napotka na delimiter (domyślnie '\n')

4

(5)

Przykład użycia get, peek, ignore

int kod;

if (cin.peek() == ’@’) // jeżeli następny znak to @ cin.ignore(); // opuść go,

else

kod = cin.get(); // wczytaj znak

(6)

Przykład z putback i getline

cout << "Please, enter a number or a word: ";

char c = cin.get();

if ( (c >= '0') && (c <= '9') ) {

int n;

cin.putback (c);

cin >> n;

cout << "You entered a number: " << n << '\n';

} else

{ string str;

cin.putback (c);

getline (cin,str);

cout << "You entered a word: " << str << '\n';

}

6

(7)

KLASA OSTREAM

(8)

Obiekty klasy ostream mogą pisać sekwencję znaków.

Z tej klasy dziedziczą:

iostream (strumień uniwersalny),

ofstream (wyjście do pliku)

ostringstream (wyjście do napisu).

8

(9)

Wybrane metody klasy ostream

int put();

//wyprowadza znak

ostream& write ( const char* s, streamsize n );

//wyprowadza n znaków z s (tablica char[])

(10)

Przykład: maszyna do pisania

#include <iostream> // std::cin, std::cout

#include <fstream> // std::ofstream

int main () {

std::ofstream outfile ("test.txt");

char ch;

std::cout << "Type some text (type a dot to finish):\n";

do {

ch = std::cin.get();

outfile.put(ch);

} while (ch!='.');

return 0;

}

// wszystko co pisze użytkownik zapisuje do pliku test.txt 10

(11)

P

RZYKŁAD

: K

OPIOWANIE PLIKU

ZNAK PO ZNAKU

main() {

ifstream we(”TEST1.TXT”);

ofstream wy(”TEST2.TXT”);

if (!we || !wy) {

cerr << ”Błąd otwarcia pliku”;

return 1;

}

char zn;

while (we.get(zn) && wy.put(zn));

}

(12)

M ANIPULATORY STRUMIENIOWE

12

(13)

M ANIPULATORY STRUMIENIOWE

Manipulatory, to funkcje zmieniające stan strumienia Większość zdefiniowana jest w pliku nagłówkowym

<iomanip.h>. Można również definiować własne manipulatory.

Częściej używane to:

endl - przejście do nowego wiersza

ends - dodanie znaku ’\0’ do strumienia dec - postać dziesiętna liczby

hex - postać szesnastkowa liczby oct - postać ósemkowa liczby

flush - opróżnienie bufora strumienia setw(int w) - ustawienie szerokości pola

setprecision(int p) - ustawienie liczby miejsc po przecinku setfill(int c) - określenie znaku wypełniającego

(14)

Przykłady użycia manipulatorów

int n= 255;

cout << hex << n << endl;

// wypisuje liczbę szesnastkową

double a=1.2355, b=2.5, c=3.14;

cout << setprecision(2) << fixed << a << '\t' << b <<

'\t' << c << endl;

// wypisuje liczby 2 znakami po przecinku, rozdzielone tabulacją

cout << scientific << a << '\t' << b << '\t' << c <<

endl;

// wypisuje liczby w formacie wykładniczym, precyzja jak przedtem

14

(15)

STRUKTURY W C++

(16)

Struktury w C++ są deklarowane tak jak w C, jednak oprócz pól mogą zawierać także funkcje (tzw. metody) i mogą dziedziczyć z innych klas i struktur.

Struktura różni się w C++ od klasy wyłącznie domyślnym zakresem

widoczności jej pól i metod - dla klasy jest to private a dla struktur public.

Używanie struktur zamiast klas nie jest dobrą praktyką programowania, gdyż utrudnia czytanie programu.

16

(17)

Przykład struktury w C++

struct miasto {

long ludnosc;

char* rzeka;

miasto(long ll,char *rz): ludnosc(ll), rzeka(strdup(rz)) {};

inline char* get_rzeka() {return rzeka};

~miasto()

{ free((void*)rzeka);

} };

(18)

DOMYŚLNE WARTOŚCI PARAMETRÓW

18

(19)

Domyślne wartości parametrów

W C++ można zdefiniować domyślne wartości parametru/ów.

W tym celu w nagłówku

funkcji/metody/konstruktora na

parametrze formalnym wykonujemy podstawienie.

Parametry domyślne muszą występować na końcu listy argumentów.

W wywołaniu możemy opuścić jedną lub więcej wartość parametru aktualnego, dla której zdefiniowano domyślną

wartość

(20)

Przykład domyślnych wartości parametrów

class Punkt {

float x,y;

public:

Punkt(float xx = 5, float yy=7):x(xx), y(yy) {

} };

Punkt p1(1,3), p2(8), p3;

20

(21)

KONSTRUKTORY I

DESTRUKTORY KLAS

POCHODNYCH

(22)

Konstruktory i destruktory klas pochodnych

Konstruktory nie są dziedziczone.

Jeśli w klasie nie zdefiniowaliśmy konstruktora, to zostanie użyty

konstruktor domyślny.

Aby powstał obiekt klasy pochodnej, musi być najpierw utworzony

podobiekt klasy nadrzędnej wchodzący w jego skład.

22

(23)

Standardowo wywoływany jest konstruktor bezparametrowy (lub domyślny) klasy nadrzędnej.

Aby do konstrukcji podobiektu klasy bazowej użyć konstruktora

innego niż bezparametrowy musimy w klasie pochodnej zdefiniować

konstruktor, który wywoła odpowiedni konstruktor klasy bazowej poprzez

listę inicjalizacyjną

(24)

Przykład takiego wywołania

class Point { protected:

int x;

int y;

Point(int x, int y) : x(x), y(y) { }

};

class Pixel: public Point { public:

int color;

Pixel(int x, int y, int color) : Punkt(x,y), color(color) { }

};

24

(25)

Składowe odziedziczone są tu inicjowane w swojej bazowej klasie (przekazywane jako parametr), składowe „nowe” w klasie

pochodnej.

Konstruktory wywoływane są „od góry”

struktury dziedziczenia.

Takie łańcuchy wywołań konstruktorów są tworzone dla każdego nowo tworzonego obiektu.

Jeśli klasa dziedziczy z kilku klas, to

konstruktory klas bazowych są wywoływane w kolejności ich wystąpienia na liście

dziedziczenia (problematyczne).

(26)

Destruktory wywoływane są w

kolejności odwrotnej do konstruktorów („od dołu” struktury dziedziczenia).

26

(27)

KONSTRUKTOR

KOPIUJĄCY

(28)

Definiowanie własnego konstruktora kopiującego jest niezbędne, gdy klasa zawiera jakieś dane dynamiczne (tworzy obiekty dynamiczne, a nawet zwykłego c-stringa)

Podobnie niezbędny jest wtedy destruktor

Konstruktor kopiujący przyjmuje jako parametr stały obiekt aktualnej klasy (przekazywany przez stałą referencję)

28

(29)

Przykład danych dynamicznych

class Osoba {

char* imie;

public:

Osoba() : imie(strcpy(new char[9], "nieznane")) { } Osoba(const char* n) : imie(strcpy(new char[strlen(n)+1], n))

{ }

Osoba(const Osoba& os) :

imie(strcpy(new char[strlen(os. imie)+1], os.

imie))

{ } ~Osoba() {

delete [ ] imie;

} };

(30)

POLIMORFIZM

30

(31)

Polimorfizm

Polimorfizm - wskaźniki i referencje mogą

dotyczyć obiektów różnego typu, a wywołanie metody dla referencji spowoduje zachowanie odpowiednie dla rzeczywistego typu

obiektu wywoływanego.

Jeśli dzieje się to w czasie działania programu, to nazywa się to późnym

wiązaniem lub wiązaniem dynamicznym.

Niektóre języki udostępniają bardziej

statyczne (w trakcie kompilacji) rozwiązania polimorfizmu - na przykład przeciążanie

operatorów i szablony w C++.

(32)

Typy statyczne i dynamiczne

class A {

public:

void fun() { cout << "Metoda z klasy A"<< endl; } };

class B : public A {

void fun() { cout << "Metoda z klasy B"<< endl; } };

A a, *wskA = new A, *wskB = new B;

A jest obiektem statycznym klasy A.

wskA jest wskaźnikiem typu A* do obiektu klasy A. Typem statycznym obiektu wskazywanego przez pa jest A i typem dynamicznym również A

wskB jest wskaźnikiem typu A* do obiektu klasy B. Typem statycznym obiektu wskazywanego przez wskB jest A, ale typem dynamicznym jego typ prawdziwy, czyli B;

32

(33)

Wywołując „zwykłą” metodę fun() dla obiektów a, *wskA, *wskB, decydować będzie typ statyczny obiektu, czyli

wywoła się treść z klasy A.

Aby wywoływać metodę fun()

odpowiednią dla dynamicznego typu obiektu należy w klasie bazowej dodać słówko virtual. Metodę nazywamy

wirtualną, a wywołanie polimorficznym.

(34)

Metoda wirtualna i wywołanie polimorficzne

class A {

public:

virtual void fun() { cout << "Metoda z klasy A"<< endl; }

};

class B : public A {

void fun() { cout << "Metoda z klasy B"<<

endl; } };

...

A *wskB->fun(); //wypisze "Metoda z klasy B"

34

(35)

Warunki wystąpienia

polimorfizmu (późnego wiązania) w C++

1.

wywołanie jest poprzez wskaźnik lub referencję typu bazowego (tutaj A*);

2.

prawdziwym typem obiektu, na rzecz którego następuje wywołanie, jest

typ pochodny (tutaj B);

3.

metoda jest wirtualna (virtual);

4.

metoda została przedefiniowana w klasie B (ta sama nazwa i

sygnatura!).

(36)

Jawne wywołanie

niepolimorficzne funkcji wirtualnej

Wywołujemy metodę z klasy bazowej przez nazwę kwalifikowaną:

wskB>A::fun();

Nie można jawnie wywoływać polimorficznie:

a.B::fun() // błąd!

wskB>B::fun(); // błąd!

36

(37)

Cena polimorfizmu

Ceną za polimorfizm jest pewna utrata

wydajności wykonania oraz narzut pamięci.

Dla wywołań na rzecz obiektów klas

niepolimorficznych odpowiednia metoda jest wybierana (i włączana do kodu

wykonywalnego) już w czasie kompilacji na podstawie typu statycznego, jest to tzw.

wczesne wiązanie.

Późne wiązanie oznacza konieczność sprawdzenia rzeczywistego typu obiektu podczas wykonania programu i wybranie odpowiedniej metody.

(38)

Inne języki obiektowe

W większości języków obiektowych (np. Java, Python) wszystkie

wywołania metod są polimorficzne (wirtualne).

W C++ mamy możliwość stosowania szybszego i lżejszego pamięciowo

statycznego wiązania (kiedy

niepotrzebny jest polimorfizm).

38

(39)

Polimorfizm i referencje

W wywołaniach polimorficznych C++

można używać referencji (analogicznie do wskaźników).

A a, *wskA = new A, *wskB = new B;

A &ref_a = a, &ref_aa = wskA, &ref_b = wskB;

ref_a.fun();

ref_aa.fun();

ref_b.fun(); //potencjalnie wirtualne

Cytaty

Powiązane dokumenty

– osoba, która poprawia zaliczenie (końcowe lub cząstkowe), może uzyskać co najwyżej ocenę bardzo dobrą.

rok akademicki 2019/20 semestr zimowy..

 Przeszukiwany jest stos wywołań funkcji w poszukiwaniu takiej, która zawiera obsługę wyjątku danego typu (czyli odpowiednią instrukcję catch).. ◦ Jeżeli

• Parametrami szablonów mogą być również szablony klas, jako tak zwane szablony parametrów szablonów.. Stack&lt;int, std::vector&gt;

 OutputIterator set_union (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result);.  OutputIterator

Model dziedziny (uzupełniony): klasy, atrybuty klas oraz

 Jednostka programu, która zadeklarowała instancję klasy (obiekt), ma dostęp do publicznych bytów tej klasy, ale tylko poprzez tę instancję.  Każda instancja klasy ma

 Symbole pojawiające się wyłącznie po prawej stronie to symbole terminalne.  Generalnie symbole terminalne to symbole z alfabetu definiowanego języka,