23
Programowanie obiektowe Klasy w C++

Programowanie obiektowe

  • Upload
    rafael

  • View
    39

  • Download
    0

Embed Size (px)

DESCRIPTION

Programowanie obiektowe. Klasy w C++. Wstęp. Klasa jest kluczową koncepcją języka C++, realizującą abstrakcję danych na bardzo wysokim poziomie. - PowerPoint PPT Presentation

Citation preview

Page 1: Programowanie obiektowe

Programowanie obiektowe

Klasy w C++

Page 2: Programowanie obiektowe

Wstęp• Klasa jest kluczową koncepcją języka C++, realizującą

abstrakcję danych na bardzo wysokim poziomie. • Odpowiednio zdefiniowane klasy stawiają do dyspozycji

użytkownika wszystkie istotne mechanizmy programowania obiektowego: ukrywanie informacji, dziedziczenie, polimorfizm z wiązaniem późnym, a także szablony klas i funkcji.

• Klasa jest deklarowana i definiowana z jednym z trzech słów kluczowych: class, struct i union.

• Elementami składowymi klasy mogą być struktury danych różnych typów, zarówno podstawowych, jak i zdefiniowanych przez użytkownika, a także funkcje dla operowania na tych strukturach. Dostęp do elementów klasy określa zbiór reguł dostępu.

Page 3: Programowanie obiektowe

Deklaracja i definicja klasy• Klasa jest typem definiowanym przez użytkownika. • Deklaracja klasy składa się z nagłówka, po którym następuje ciało

klasy, ujęte w parę nawiasów klamrowych; po zamykającym nawiasie klamrowym musi wystąpić średnik, ewentualnie poprzedzony listą zmiennych.

• W nagłówku klasy umieszcza się słowo kluczowe class (lub struct albo union), a po nim nazwę klasy, która od tej chwili staje się nazwą nowego typu.

• Klasa może być deklarowana: Na zewnątrz wszystkich funkcji programu. Zakres widzialności takiej klasy

rozciąga się na wszystkie pliki programu. Wewnątrz definicji funkcji. Klasę taką nazywa się lokalną, ponieważ jej zakres

widzialności nie wykracza poza zasięg funkcji. Wewnątrz innej klasy. Klasę taką nazywa się zagnieżdżoną, ponieważ jej zakres

widzialności nie wykracza poza zasięg klasy zewnętrznej.

• Deklaracji klas nie wolno poprzedzać słowem kluczowym static.

Page 4: Programowanie obiektowe

Przykłady deklaracji• Przykładowe deklaracje klas mogą mieć postać:

class Pusta {};

class Komentarz { /* Komentarz */};

class Niewielka { int n; };

• Wystąpienia klasy deklaruje się tak samo, jak zmienne innych typów, np.Pusta pusta1, pusta2;

Niewielka nw1, nw2;

Niewielka* wsk = &nw1;

• Zmienne pusta1, pusta2, nw1, nw2 nazywają się obiektami, zaś wsk jest wskaźnikiem do typu Niewielka, zainicjowanym adresem obiektu nw1.

• Klasy w rodzaju Pusta i Komentarz używa się często jako klasy-makiety podczas opracowywania programu.

Page 5: Programowanie obiektowe

Specyfikatory (modyfikatory) dostępu• Deklaracje elementów składowych klasy można poprzedzić

etykietą public:, protected:, lub private:. • Jeżeli sekwencja deklaracji elementów składowych nie jest

poprzedzona żadną z tych etykiet, to kompilator przyjmuje domyślną etykietę private:. Oznacza to, że dana składowa może być dostępna jedynie dla funkcji składowych i tzw. funkcji zaprzyjaźnionych klasy, w której jest zadeklarowana.

• Wystąpienie etykiety public: oznacza, że występujące po niej nazwy deklarowanych składowych mogą być używane przez dowolne funkcje, a więc również takie, które nie są związane z deklaracją danej klasy. Znaczenie etykiety protected: - później.

• Ze względu na sterowanie dostępem do składowych klasy, słowa kluczowe public, protected i private nazywa się specyfikatorami dostępu.

Page 6: Programowanie obiektowe

Przykład#include <iostream>

#include <conio.h>

using namespace std;

class Niewielka {

public:

int n;

void komunikat()

{

cout<<"Tu jestesmy";

}

};

void main() {

Niewielka nw1;

nw1.n = 5;

cout<<"nw1.n="<<nw1.n<<endl;

cout<<"komunikat=";

nw1.komunikat();

getch();

}

Page 7: Programowanie obiektowe

Definicja klasy• Jeżeli klasa zawiera funkcje składowe, to ich deklaracje muszą

wystąpić w deklaracji klasy. Funkcje składowe mogą być jawnie deklarowane ze słowami kluczowymi inline, static i virtual; nie mogą być deklarowane ze słowem kluczowym extern.

• W deklaracji klasy można również umieszczać definicje krótkich (1-2 instrukcje) funkcji składowych; są one wówczas traktowane przez kompilator jako funkcje rozwijalne, tj. tak, jakby były poprzedzone słowem kluczowym inline.

• W programach jednoplikowych dłuższe funkcje składowe deklaruje się w nawiasach klamrowych zawierających ciało klasy, zaś definiuje się bezpośrednio po zamykającym nawiasie klamrowym.

• Deklaracja klasy wraz z definicjami funkcji składowych (i ewentualnie innymi wymaganymi definicjami) stanowi definicję klasy.

Page 8: Programowanie obiektowe

Przykład definicji klasy#include <iostream>#include <conio.h>using namespace std;

class Punkt { public: int x,y; void init(int a, int b) { x = a; y = b; } void ustaw(int, int);};void Punkt::ustaw(int a, int b){ x = x + a; y = y + b;}int main() {

Punkt punkt1;punkt1.init(1,1);cout << punkt1.x << endl;

cout << punkt1.y << endl;punkt1.ustaw(10,15);cout << punkt1.x << endl;cout << punkt1.y << endl;

getch();return 0;

}

Page 9: Programowanie obiektowe

Konstruktory i destruktory• Konstruktory i destruktory należą do grupy specjalnych funkcji

składowych. Grupa ta obejmuje: konstruktory i destruktory inicjujące, konstruktor kopiujący oraz tzw. funkcje operatorowe.

• Konstruktor jest funkcją składową o takiej samej nazwie, jak nazwa klasy. Nazwą destruktora jest nazwa klasy, poprzedzona znakiem tyldy (~).

• Każda klasa zawiera konstruktor i destruktor, nawet gdy nie są one jawnie zadeklarowane. Zadaniem konstruktora jest konstruowanie obiektów swojej klasy; jego wywołanie w programie pociąga za sobą:

alokację pamięci dla obiektu; przypisanie wartości do niestatycznych zmiennych składowych; wykonanie pewnych operacji porządkujących (np. konwersji

typów).• Jeżeli w klasie nie zadeklarowano konstruktora i destruktora, to zostaną

one wygenerowane przez kompilator i automatycznie wywołane podczas tworzenia i destrukcji obiektu.

Page 10: Programowanie obiektowe

Przykład#include <iostream>

using namespace std;

class Punkt {

public:

Punkt(int, int);

void ustaw(int, int);

private:

int x,y;

};

Punkt::Punkt(int a, int b)

{ x = a; y = b; }

void Punkt::ustaw( int c, int d)

{ x = x + c; y = y + d; }

int main() {

Punkt punkt1(3,4); //konstruktor

return 0;

}

Page 11: Programowanie obiektowe

Własności konstruktorów i destruktorów

• Konstruktory i destruktory generowane przez kompilator są public.

• Konstruktory definiowane przez użytkownika również powinny być public, aby istniała możliwość tworzenia obiektów na zewnątrz deklaracji klasy.

• W pewnych przypadkach szczególnych konstruktory mogą wystąpić po etykiecie protected:, a nawet private:.

• Konstruktor jest wołany automatycznie, gdy definiuje się obiekt; destruktor gdy obiekt jest niszczony.

• Z bloku konstruktora i destruktora mogą być wywoływane funkcje składowe ich klasy.

Page 12: Programowanie obiektowe

Kiedy wykorzystujemy konstruktor

• Konstruktor jest wołany wtedy, gdy ma być utworzony obiekt jego klasy. Obiekt taki może być tworzony na wiele sposobów: jako zmienna globalna; jako zmienna lokalna; przez jawne użycie operatora new; jako obiekt tymczasowy; jako zmienna składowa, zagnieżdżona w innej

klasie.

Page 13: Programowanie obiektowe

Przykład#include <iostream>

#include <conio.h>

using namespace std;

class Status {

public:

Status() { licznik++; }

~Status() { licznik--;}

int odczyt() { return licznik; }

private:

static licznik; //deklaracja

};

int Status::licznik = 0; // Definicja

int main() {

Status ob1,ob2, ob3;

cout <<"Mamy"<< ob1.odczyt() << " obiekty\n"; //mamy 3 obiekty

Status *wsk;

wsk = new Status; //Alokuj dynamicznie

if (!wsk) {

cout << "Nieudana alokacja\n";

return 1;

}

cout << "Mamy "<< ob1.odczyt()<<" obiekty po alokacji\n" ;// mamy 4 obiekty po alokacji

// skasuj obiekt

delete wsk;

cout << "Mamy "<< ob1.odczyt()<<"obiekty po destrukcji\n"; // mamy 3 obiekty po destrukcji

getch();

return 0;

}

Page 14: Programowanie obiektowe

Przeciążanie konstruktorów• Konstruktory, podobnie jak “zwykłe” funkcje (także

funkcje składowe klasy), mogą być przeciążane. • Najczęściej ma to na celu tworzenie obiektów

inicjowanych zadanymi wartościami zmiennych składowych, lub też obiektów bez podanych wartości inicjalnych.

• Dzięki temu można zadeklarować więcej niż jeden konstruktor dla danej klasy.

• Wywołanie konstruktora w deklaracji obiektu pozwala kompilatorowi ustalić, w zależności od liczby i typów podanych argumentów, którą wersję konstruktora należy wywołać.

Page 15: Programowanie obiektowe

Przykład#include <iostream>

using namespace std;

class Punkt {

public:

Punkt() {}

Punkt(int a, int b): x(a), y(b) {}

int x,y;

};

int main() {

Punkt punkt1(3,4);

cout << punkt1.x << endl;

Punkt punkt2;

cout << punkt2.x << endl;

return 0;

}

Page 16: Programowanie obiektowe

Argumenty domyślne• Alternatywą dla przeciążania konstruktora jest wyposażenie

go w argumenty domyślne, podobnie jak jest to możliwe dla zwykłych funkcji. Argument domyślny jest przyjmowany przez kompilator wtedy, gdy w wywołaniu funkcji nie podano odpowiadającego mu argumentu aktualnego.

• Wartości domyślne argumentów formalnych w deklaracji (nie w definicji!) zapisujemy w następujący sposób:

Jeżeli w deklaracji konstruktora umieszczono nazwy argumentów formalnych, to po nazwie argumentu piszemy znak równości, a po nim odpowiednią wartość, np. Punkt ( int a = 0, int b = 0 );

Jeżeli deklaracja nie zawiera nazw argumentów, a jedynie ich typy, to znak równości i następującą po nim wartość piszemy po identyfikatorze typu, np. Punkt ( int = 0, int = 0 );

Page 17: Programowanie obiektowe

Przykład#include <iostream>

#include <conio.h>

using namespace std;

class Punkt {

public:

int x,y;

Punkt(int = 0, int = 0);

};

Punkt::Punkt(int a, int b): x(a), y(b)

{

if (a==0 && b==0)

cout << "Obiekt bez inicjowania...\n";

else cout << "Obiekt z inicjowaniem...\n";

}

int main() {

Punkt punkt1(3,4);

cout << punkt1.x << endl; //x=3

Punkt punkt2;

cout << punkt2.x << endl; //x=0

getch();

return 0;

}

Page 18: Programowanie obiektowe

Przeciążanie operatorów• W języku C++ są duże możliwości, spotykane w nielicznych współczesnych

językach programowania.

• Ma on do dyspozycji mechanizm, który pozwala tworzyć nowe definicje dla istniejących operatorów.

• Dzięki temu programista może tak zmodyfikować działanie operatora, aby wykonywał on operacje w narzucony przez niego sposób.

• Jest to szczególnie istotne w programach czysto obiektowych. Np. obiekty wielokrotnie redefiniowanej klasy Punkt możemy uważać za wektory w prostokątnym układzie współrzędnych. W geometrii i fizyce dla takich obiektów są np. określone operacje dodawania i odejmowania. Byłoby wskazane zdefiniować podobne operacje dla deklarowanych klas przez programistę.

• Wykorzystamy do tego celu następującą ogólną postać definicji tzw. funkcji operatorowej, tj. funkcji, która definiuje pewien operator “@”:

typ klasa::operator@(argumenty)

{ // wykonywane operacje }

Page 19: Programowanie obiektowe

Reguły przeciążania operatorów• Operator @ jest zawsze przeciążany względem klasy, w której jest

zadeklarowana jego funkcja operator@. Zatem w innych kontekstach operator nie traci żadnego ze swych oryginalnych znaczeń, ustalonych w definicji języka, natomiast zyskuje znaczenia dodatkowe.

• Funkcja definiująca operator musi być albo funkcją składową klasy, albo mieć co najmniej jeden argument będący obiektem lub referencją do obiektu klasy (wyjątkiem są funkcje, które redefiniują znaczenie operatorów new i delete). Ograniczenie to gwarantuje, że wystąpienie operatora z argumentami typów nie będących klasami, jest jednoznaczne z wykorzystaniem jego standardowej, wbudowanej w język definicji. Jego intencją jest rozszerzanie języka, a nie zmiana wbudowanych definicji.

• Nie mogą być przeciążane operatory “.”, “.*”, “::”, “?:”, “sizeof” oraz symbole “#” i “##”.

• Nie jest możliwa zmiana priorytetu, reguł łączności, ani liczby argumentów operatora.

Page 20: Programowanie obiektowe

Przykład// Operator dodawania +

#include <iostream>

using namespace std;

class Punkt {

public:

Punkt(): x(0),y(0){} // zapis x(0) oznacza x=0 -- został wzięty z języka Simula

Punkt(int a, int b): x(a),y(a) {} //x(a) jw.

int fx() { return x; }

int fy() { return y; }

Punkt operator+(Punkt);

private:

int x,y;

};

Punkt Punkt::operator+(Punkt p)

{

return Punkt(x + p.x +7, y + p.y);

}

int main() {

Punkt punkt1, punkt2, punkt3; //konstruktor Punkt()

punkt1 = Punkt(2,2);

punkt2 = Punkt(3,1);

punkt3 = punkt1 + punkt2; //przeciążony

cout<<"punkt1.x="<< punkt1.fx() << endl; //2cout << "punkt3.x= " << punkt3.fx() << endl; //12

int i = 10, j;

j = i + 15; // znany '+'

cout << " j= " << j << endl;

return 0;

}

Page 21: Programowanie obiektowe

Przykład// Operator relacyjny ==

#include <iostream>

using namespace std;

class Punkt {

public:

Punkt(): x(0),y(0) {}

Punkt(int a, int b): x(a),y(a) {}

int fx() { return x; }

int fy() { return y; }

int operator==(Punkt);

private:

int x,y;

};

int Punkt::operator==(Punkt p)

{

if( p.fx() == fx() && p.fy() == fy() )

return (-1);

else return (0);

}

int main() {

Punkt punkt1, punkt2;

punkt1 = Punkt(2,2);

punkt2 = Punkt(3,1);

if ( punkt1 == punkt2 ) cout <<„ Rowne\n”;

else cout <<„Nierowne\n”;

int i = 5, j = 6, k;

k = i == j; // predefiniowany '=='

cout <<„k=„<< k << endl;

return 0;

}

• Wyniki• Nierowne• k= 0

Page 22: Programowanie obiektowe

Język C++

Dziedziczenie

Page 23: Programowanie obiektowe

Przykład#include <iostream>

Using namespace std;

class Bazowa {

public:

void ustawx(int n): x(n) {}

void podajx() { cout << x << "\n"; }

private:

int x,z;

};

class Pochodna : public Bazowa {

public:

void ustawy(int m) { y = m; }

void podajy() { cout << y << "\n"; }

private:

int y;

};

int main() {

Pochodna ob;

ob.ustawx(10);

ob.ustawy(20);

ob.podajx();

ob.podajy();

cout << sizeof(Bazowa) << endl; //4B (2*int)

cout << sizeof(Pochodna) << endl; //6B (2*int+1*int)

cout << sizeof ob << endl; //6B

return 0;

}