Programowanie obiektowe, wykład nr 7 Przegląd typów strukturalnych

Transkrypt

Programowanie obiektowe, wykład nr 7 Przegląd typów strukturalnych
Dr hab. inż. Lucyna Leniowska, prof. UR, Zakład Mechatroniki, Automatyki i Optoelektroniki, IT
Programowanie obiektowe, wykład nr 7
Przegląd typów strukturalnych - klasy i obiekty - c.d.
Klasa - powtórzenie
Klasa pozwala grupowad w programach - w jednej zmiennej - dane obiektu
i metody (funkcje), które operują na tych danych.





Aby zdefiniowad klasę, należy podad w programie słowo kluczowe
class, nazwę klasy, składowe danych klasy i funkcje (metody) klasy.
Definicja klasy tworzy wzorzec, przy użyciu którego można w programach
tworzyd obiekty typu tej klasy, analogicznie jak zmienne typów char, int
itd.
Aby przypisywad w programach wartości składowym danych klasy, należy
używad operatora wyboru składowej oznaczanego kropką.
Do wywoływania funkcji składowych klasy także służy operator wyboru
składowej oznaczany kropką.
Aby zdefiniowad funkcję na zewnątrz definicji klasy, należy w programie
poprzedzid definicję funkcji nazwą klasy i operatorem widoczności (::) w
nastepujący sposób:
typ_wartosci nazwa_klasy::nazwa_funkcji (parametry)
{
// Instrukcje
}


Składowe klasy mogą byd albo publiczne, albo prywatne. Programy mają
bezpośredni dostęp do składowych publicznych przy użyciu operatora
wyboru składowej (kropka). Natomiast dostęp do składowych
prywatnych mogą realizowad jedynie za pośrednictwem metod klasy.
Jeśli słowo kluczowe public nie występuje jawnie, to C++ przyjmuje, że
wszystkie składowe są prywatne.
Konstruktory i destruktory c.d.



Konstruktory i destruktory to specjalne funkcje klasy, które są
automatycznie wywoływane podczas tworzenia lub usuwania obiektów.
Tworząc w programie obiekt, można przekazywad parametry funkcji
konstruktor w deklaracji obiektu.
Destruktor jest specjalną funkcją, którą program automatycznie
wywołuje za każdym razem, gdy obiekt jest usuwany. Destruktor ma taką
samą nazwę jak klasa obiektu, poprzedzoną falką (~).
Podawanie domyślnych wartości funkcjom konstruktor
 W C++ można podawać domyślne wartości parametrów funkcji.
 Jeśli nie podamy w wywołaniu funkcji/metody wartości żadnego
parametru, to będą wykorzystywane wartości domyślne, o ile zostały
zdefiniowane.
 W konstruktorach można podawać w definicjach domyślne wartości
parametrów tak samo jak dla innych funkcji.
Konstruktor o wartościach domyślnych
pracownik::pracownik (std::string imie_nazwisko,
long ident_pracownika, float zarobki=2000.0)
Możliwe i poprawne jest wywołanie:
1) pracownik sekretarka ("Anna Kowal", 101);
2) pracownik sekretarka ("Anna Kowal", 101, 2000.0);
3) pracownik sekretarka ("Anna Kowal", 101, 5000.0);
Przeciążanie funkcji konstruktor
 W C++ można przeciążad funkcje, definiując funkcje o tych samych
nazwach, ale o różnych typach parametrów.
 W taki sam sposób można przeciążad funkcję konstruktor.
W przedstawionym poniżej programie występuje przeciążona funkcja
konstruktor.
Przykład 7.1.
#include <iostream>
class pracownik {
public:
pracownik (std::string, long, float);
pracownik (std::string, long);
~pracownik (void);
void inf_o_prac (void);
int zmien_zarobki (float);
long podaj_id (void);
private:
std::string imie_nazwisko;
long ident_pracownika;
float zarobki;
};
pracownik::pracownik (std::string imie_nazwisko,
long ident_pracownika, float zarobki)
{
pracownik::imie_nazwisko = imie_nazwisko;
pracownik::ident_pracownika = ident_pracownika;
if (zarobki < 7000.00)
pracownik::zarobki = zarobki;
else
pracownik::zarobki = 0.0;
//Podano niepoprawne zarobki
}
pracownik::pracownik (std::string imie_nazwisko,
long ident_pracownika)
{
pracownik::imie_nazwisko = imie_nazwisko;
pracownik::ident_pracownika = ident_pracownika;
do {
std::cout << "Podaj zarobki pracownika " << imie_nazwisko
<< "mniejsze od 7 000: " ;
std::cin >> pracownik::zarobki;
} while (pracownik::zarobki >= 7000.0);
}
pracownik::~pracownik () {
std::cout << "Usuwam obiekt " << imie_nazwisko <<std::endl;
}
void pracownik::inf_o_prac (void)
{
std::cout << "Imię i nazwisko: " << imie_nazwisko <<std::endl;
std::cout << "Ident: " << ident_pracownika << std::endl;
std::cout << "Zarobki: " << zarobki << std::endl;
}
int main(void)
{
pracownik sekretarka ("Anna Kowal", 101, 3000.0);
pracownik ksiegowa ("Katarzyna Kwiatek", 200, 5000.0);
pracownik menager ("Jan Kowalski", 102); //Tworzenie obiektu3
sekretarka.inf_o_prac ();
ksiegowa.inf_o_prac ();
menager.inf_o_prac();
return 0;
}
W przypadku inicjacji obiektu menager za pomocą konstruktora o dwóch
parametrach (przeciążonego), na ekranie zobaczymy pytanie o wartośd
trzeciego parametru:
Podaj zarobki pracownika Jan Kowalski mniejsze od 7 000:
4500
Imię i nazwisko: Anna Kowal
Ident: 101
Zarobki: 3000
Imię i nazwisko: Katarzyna Kwiatek
Ident: 200
Zarobki: 5000
Imię i nazwisko: Jan Kowalski
Ident: 102
Zarobki: 4500
Usuwam obiekt Jan Kowalski
Usuwam obiekt Katarzyna Kwiatek
Usuwam obiekt Anna Kowal
Porównanie typów złożonych: struktura struct i klasa class
podobieostwa i różnice
 W przypadku stosowania zmiennych typu
związane ze strukturą.
struct
funkcje nie są
#include <cmath>
#include <iostream>
struct wektor2d {
double x;
double y;
};
double dlugosc(const wektor2d& wektor) {
return sqrt(wektor.x*wektor.x +
wektor.y*wektor.y);
}
 Gdy stosujemy typy
class to funkcje(metody) przynależą do klasy.
class wektor2d {
public:
double x;
double y;
double dlugosc() const {
return sqrt(x*x + y*y);
}
}
Uwaga: Słowo kluczowe const sygnalizuje, czy w metodzie możemy
zmieniad składowe obiektu, na rzecz którego została wywołana metoda.
 Teoretycznie nie ma żadnej szczególnej różnicy pomiędzy kodem
umieszczonym w metodzie należącej do klasy i kodem zwykłej funkcji,
która współpracuje ze strukturą. Efekty działania są takie same.
 Podstawowe różnice istnieją w czytelności zapisu i w różnych
udogodnieniach, które umożliwiają klasy.
 Praktyczne różnice istnieją np. przy optymalizacjach, które kompilator
potrafi zastosowad, a także przy użyciu metod wirtualnych i wskaźników
do funkcji.
 Stosujemy następującą składnię:
o dla struktur:
naz_funkcji(ob, parametry),
o dla klas:
ob.naz_funkcji(parametry),
Przykład 7.2.
a)Gdy wektor2d jest strukturą
struct wektor2d {
double x;
double y;
};
wektor2d suma(const wektor2d& a,
const wektor2d& b) {
wektor2d wynik;
wynik.x = a.x + b.x;
wynik.y = a.y + b.y;
return wynik;
}
void negacja(wektor2d& wektor) {
wektor.x = -wektor.x;
wektor.y = -wektor.y;
}
b)Gdy wektor2d jest klasą
class wektor2d {
public:
double x;
double y;
double dlugosc() const {
return sqrt(x*x + y*y);
}
wektor2d suma(const wektor2d& b) const {
wektor2d wynik;
wynik.x = x + b.x;
wynik.y = y + b.y;
return wynik;
}
void negacja() { // bez 'const'
x = -x;
y = -y;
}
void wypisz(std::ostream& out) const {
out << "(" << x
<< "," << y << ")";
}
};
 Jeżeli parametr opisujący strukturę na której działa funkcja posiada
modyfikator const, to w klasie piszemy const za nawiasami
zawierającymi parametry, np.
o Dla struktur:
double dlugosc(const wektor2d& wektor) { … }
o Dla klas:
double dlugosc() const { … }
 W programach - jeżeli można i ma to sens, warto dodawad modyfikatory
const, bo w ten sposób:
o metody będą ogólniejsze,
o będzie można je wywoład dla większej liczby obiektów,
o np. metodę dlugosc()const możemy wywoład na rzecz
obiektu const wektor2d, ale już metodę negacja() nie.
Przyjrzyjmy się jeszcze metodzie wypisz()
Dla struktur:
void wypisz(const wektor2d wektor, std::ostream& out) {
out << "(" << wektor.x << "," << wektor.y << ")";
}
Dla klas:
void wektor2d::wypisz(std::ostream& out) const {
out << "(" << x << "," << y << ")";
}
W tej funkcji parametr out jest strumieniem wyjścia, tak aby dało się
wykorzystad metodę zarówno z std::cout, jak i z plikami.
Wywołanie wygląda zatem następująco:
dla struktur: wypisz(c, std::cout);
a dla klas: c.wypisz(std::cout);
A gdyby dane pochodziły z pliku, to wywołanie przyjęłoby postad:
dla struktur: std::fstream f ("log.txt"); wypisz(c, f);
a dla klas std::fstream f ("log.txt"); c.wypisz(f);
Przykład 7.3
Poniżej przedstawiony jest przykład implementacji wektora na płaszczyźnie za
pomocą struktury i za pomocą klasy. Dla wektora zaplanowano następujące
operacje:




dodawanie, za pomocą funkcji suma
zmiana znaku składowych, za pomocą funkcji negacja
obliczenie długości wektora, za pomocą funkcji dlugosc
wypisanie wektora na dowolny strumieo (np. na std::cout lub do pliku).
#include <cmath>
#include <iostream>
#include <cmath>
#include <iostream>
struct wektor2d {
class wektor2d {
public:
double x;
double y;
double x;
double y;
};
double dlugosc(const wektor2d& wektor) {
return sqrt(wektor.x*wektor.x +
wektor.y*wektor.y);
}
double dlugosc() const {
return sqrt(x*x + y*y);
}
wektor2d suma(const wektor2d& a,
const wektor2d& b) {
wektor2d wynik;
wynik.x = a.x + b.x;
wynik.y = a.y + b.y;
return wynik;
}
wektor2d suma(const wektor2d& b) const {
void negacja(wektor2d& wektor) {
wektor.x = -wektor.x;
wektor.y = -wektor.y;
}
void negacja() { // bez 'const'
x = -x;
y = -y;
}
void wypisz(const wektor2d wektor,
std::ostream& out) {
out << "(" << wektor.x
<< "," << wektor.y << ")";
}
void wypisz(std::ostream& out) const {
int main() {
wektor2d a, b, c;
a.x = 4; a.y = 6;
b.x = 1; b.y = 2;
negacja(b);
c = suma(a, b);
std::cout << "Dlugosc wektora ";
wypisz(c, std::cout);
std::cout << " wynosi: "
<< dlugosc(c)
<< std::endl;
return 0;
}
wektor2d wynik;
wynik.x = x + b.x;
wynik.y = y + b.y;
return wynik;
}
out << "(" << x
<< "," << y << ")";
}
};
int main() {
wektor2d a, b, c;
a.x = 4; a.y = 6;
b.x = 1; b.y = 2;
b.negacja();
c = a.suma(b);
std::cout << "Dlugosc wektora ";
c.wypisz(std::cout);
std::cout << " wynosi: "
<< c.dlugosc()
<< std::endl;
return 0;
}
Uwaga: następny przykład gdy klasa wektor2d ma dodatkowo różne
konstruktory będzie na następnym wykładzie.

Podobne dokumenty