• Nie Znaleziono Wyników

Programowanie i struktury danych Programowanie i struktury danych

N/A
N/A
Protected

Academic year: 2021

Share "Programowanie i struktury danych Programowanie i struktury danych"

Copied!
13
0
0

Pełen tekst

(1)

Programowanie i struktury danych Programowanie i struktury danych

Wykład 2 Wykład 2

Dr Piotr Cybula <piotr.cybula@wmii.uni.lodz.pl>

Dr Piotr Cybula <piotr.cybula@wmii.uni.lodz.pl>

(2)

Enkapsulacja / hermetyzacja Enkapsulacja / hermetyzacja

bezpieczeństwo kodu i zapewnienie spójności danych

niezależność i separacja danych

unikanie «dawania małpie brzytwy» (koncepcja czarnej skrzynki)

poziomy dostępu do struktury/klasy (specyfikatory dostępu):

public – dostęp publiczny dla innych struktur/klas i funkcji

private – dostęp prywatny, tylko dla metod struktury/klasy

protected – dostęp chroniony, dla metod struktury/klasy i jej struktur/klas pochodnych (dziedziczenie)

friend – deklaracja przyjaźni, dostęp dla wyspecyfikowanych struktur/klas lub funkcji

metody stałe (const) gwarantujące zachowanie obiektu w stanie

niezmienionym

(3)

Hermetyzacja : struct Hermetyzacja : struct

W strukturze wszystkie składowe (pola i metody) są domyślnie publiczne:

struct Student

{ private: //składowe ukryte string name;

int index;

bool checkIndex(int _index); //metoda wewnętrzna public: //interfejs publiczny

Student(string _name);

Student();

~Student();

int getIndex() const; //selektor

void setIndex(int _index); //modyfikator … //inne metody

};

Student s(”Scott Tiger”);

s.setIndex(12345); //użycie metody publicznej

s.checkIndex(); //błąd kompilacji – metoda prywatna

(4)

Hermetyzacja : class Hermetyzacja : class

W klasie wszystkie składowe (pola i metody) są domyślnie prywatne:

class Student

{ //składowe ukryte (słowo kluczowe private jest opcjonalne) string name;

int index;

bool checkIndex(int _index); //metoda wewnętrzna public: //interfejs publiczny

Student(string _name);

Student();

~Student();

int getIndex() const; //selektor

void setIndex(int _index); //modyfikator … //inne metody

};

Student s(”Scott Tiger”);

s.setIndex(12345); //użycie metody publicznej

s.checkIndex(); //błąd kompilacji – metoda prywatna

(5)

Niezmienniki klasy Niezmienniki klasy

reguły (zdania logiczne) opisujące spójny stan struktury/klasy (poprawne wartości atrybutów)

pomocnicze metody prywatne sprawdzające te reguły

każda metoda publiczna (w tym konstruktory) zakłada, że obiekt, dla którego została wywołana jest w stanie spójnym i gwarantuje pozostawienie go w takim stanie po jej zakończeniu (projektowanie oparte o kontrakt – design by

contract)

metoda publiczna zmieniająca stan obiektu (modyfikator) ma obwiązek sprawdzić zgodność takiej zmiany z regułami opisanymi za pomocą niezmienników

w przypadku przekroczenia reguł metoda zobowiązana jest zgłosić sytuację wyjątkową (throw exception)

wyjątek zgłaszony w konstruktorze zapobiega utworzeniu obiektu (brak

wywołania destruktora)

(6)

Niezmienniki klasy Niezmienniki klasy

#include <stdexcept>

class Student { string name;

int index;

float avg;

//class invariants:

//index is a positive number //avg is between 2.0 and 5.0 public: //interfejs publiczny Student(string _name);

void setIndex(int _index) //modyfikator

{ if(_index <= 0) throw invalid_argument(”Error”); //wyjątek index = _index;

}};

Student s1(”Scott Tiger”), s2(”John Smith”);

try {

s1.setIndex(12345); //ok

s2.setIndex(-1234); //zgłoszenie wyjątku

} catch(exception &e) { cout << e.what(); } //przechwycenie

(7)

Przeci ąż enie operatorów Przeci ąż enie operatorów

przedefiniowanie wbudowanego operatora dla obiektów własnej klasy (przekazywanych jako operandy)

nazwa funkcji zawiera słowo kluczowe operator z przyrostkiem identyfikującym nazwę lub symbol operatora, np. operator+, operator=, itp.

przeciążone operatory mogą być metodami klasy lub zaprzyjaźnionymi funkcjami zewnętrznymi (friend)

nie można tworzyć nowych operatorów, ani zmieniać ich priorytetów

operatory mogą uczynić strukturę bardziej intuicyjną w użyciu, szczególnie jeżeli realizuje pewne operacje matematyczne, ale często stanowią jedynie

«syntaktyczny lukier» – nie należy przeciążać operatora jeżeli jego użycie nie

zwiększa czytelności kodu

(8)

Operatory unarne Operatory unarne

operują na pojedynczym obiekcie klasy, dla którego są wywoływane

przeciążane typowo jako stała bezparametrowa metoda klasy

class Vector { double x, y;

public:

Vector(double _x = 0, double _y = 0) { x = _x; y = _y; } Vector operator-() const;

};

Vector Vector::operator-() const { return Vector(-x, -y);

}

Vector v1(2, -3);

Vector v2(-v1); //wywołanie operatorowe v2 = -v1; //tu też

v2 = v1.operator-(); //wywołanie funkcyjne

(9)

Operatory binarne Operatory binarne

operują na obiekcie klasy jako pierwszym operandzie oraz drugim operandzie podanego typu (może być tego samego)

przeciążane jako stała jednoparametrowa metoda klasy

class Vector { double x, y;

public:

Vector(double _x = 0, double _y = 0) { x = _x; y = _y; } Vector operator+(const Vector &v) const;

};

Vector Vector::operator+(const Vector &v) const { return Vector(x + v.x, y + v.y);

}

Vector v1(2, -3), v2(-1, 4), v3;

v3 = v1 + v2; v3 = v1 + 2; //wywołania operatorowe v3 = v1.operator+(v2); //wywołanie funkcyjne

(10)

Operatory binarne Operatory binarne

często przeciążane jako dwuparametrowa funkcja zaprzyjaźniona (dla zapewnienia przemienności operandów)

class Vector { double x, y;

public:

Vector(double _x = 0, double _y = 0) { x = _x; y = _y; }

friend Vector operator+(const Vector &v1, const Vector &v2);

};

Vector operator+(const Vector &v1, const Vector &v2) { return Vector(v1.x + v2.x, v1.y + v2.y);

}

Vector v1(2, -3), v2(-1, 4), v3;

v3 = v1 + v2; v3 = v1 + 2; //wywołania operatorowe

v3 = 2 + v1; //wywołanie operatorowe, możliwa przemienność v3 = v1.operator+(v2); //błąd kompilacji – to nie jest metoda v3 = operator+(v1, v2); //wywołanie funkcyjne

(11)

Operator tablicowy [ ] Operator tablicowy [ ]

musi być jednoparametrową metodą klasy

może zwracać referencję, aby umożliwić modyfikację zwracanej zmiennej

class Vector { double x, y;

public:

Vector(double _x = 0, double _y = 0) { x = _x; y = _y; } double& operator[](int i) const;

};

double& Vector::operator[](int i) const { return (i == 1) ? x : y;

}

Vector v1(2, -3), v2(-1, 4);

double d = v1[1] + v2[2]; //wywołanie do odczytu v1[1] = 5; //wywołanie do modyfikacji

(12)

Operatory strumieniowe << i >>

Operatory strumieniowe << i >>

przeciążane typowo dla strumieni wejściowych/wyjściowych

pierwszy operand jest strumieniem (ostream lub istream), więc operator powinien być funkcją zaprzyjaźnioną i powinien zwracać strumień dla umożliwienia wielokrotnych wywołań kaskadowych

class Vector { double x, y;

public:

Vector(double _x = 0, double _y = 0) { x = _x; y = _y; }

friend ostream& operator<<(ostream &stream, const Vector &v);

friend istream& operator>>(istream &stream, Vector &v);

};ostream& operator<<(ostream &stream, const Vector &v) { stream << '[' << v.x << ',' << v.y << ']';

return stream;

}Vector v1(2, -3), v2(-1, 4) ;

cout << v1 << ' ' << v2 << endl; //wywołanie kaskadowe

(13)

Operator przypisania = Operator przypisania =

musi być metodą klasy z obiektem źródłowym jako parametrem i powinien zwracać referencję na obiekt docelowy (*this) dla wywołań kaskadowych

automatycznie tworzony przez kompilator jeżeli nie został przeciążony jawnie (musi być przeciążony gdy klasa wymaga napisania własnego konstruktora kopiującego)

class Vector { double x, y;

public:

Vector(double _x = 0, double _y = 0) { x = _x; y = _y; } Vector& operator=(const Vector &v);

};Vector& Vector::operator=(const Vector &v) { if (this != &v) { x = v.x; y = v.y; } return *this;

}Vector v1(2, -3), v2, v3;

v3 = v2 = v1; //wywołanie kaskadowe

Cytaty

Powiązane dokumenty

● szablony pozwalają na wielokrotne wykorzystanie istniejącego kodu źródłowego struktury danych dla wielu wersji tej struktury z tym samym interfejsem, ale różnymi typami

● strumienie wejścia/wyjścia: iostream, fstream, sstream, iomanip. ●

(3) przestawiamy wskaźnik next w węźle wskazywanym przez pred (lub wskaźnik head gdy pred jest pusty) oraz wskaźnik prev w węźle wskazywanym przez succ (lub wskaźnik tail gdy

Powiemy, że algorytm Alg działający w strukturze danych S jest całkowicie poprawny ze względu na specyfikację &lt;wp,wk&gt; wttw dla wszystkich danych w strukturze S

Problem Dany jest ciąg rosnący e[1],..,e[n] oraz element x pewnej przestrzeni liniowo uporządkowanej &lt;E, &gt;. Następnie porównujemy x z kolejnymi elementami ciągu

(3) Jeżeli tak otrzymane drzewo nie jest częściowo uporządkowane, to przechodząc wzdłuż drogi od liścia x do korzenia, poprawić etykiety zamieniając etykietę ojca z

Każda linia w tych plikach zawiera dane pojedynczego studenta rozdzielone pojedynczymi spacjami w następującej kolejności: nazwisko, imię (imiona), numer indeksu

• w przypadku gdy dane osoby są poprawne, a niepoprawny jest jej numer PESEL (czyli nieprawidłowość wystąpiła w pliku pesele.txt), osoba ta nie jest wpisywana do