background image

Wykład 7

C++:

Przeciążanie operatorów, Wzorce 

projektowe, Zmienne dynamiczne

Wskaźniki i referencje,

Operator przypisania

Relacje, Dziedziczenie, Polimorfizm

background image

itm / MVLab (c) 2007-2011

Przeciążanie operatorów

Powód:

l

Dla standardowych typów w C istnieje szereg zdefiniowanych 
operatorów

l

Dla nowo tworzonych typów (klas) powinna być także 

możliwość zdefiniowania analogicznych, intuicyjnych 
operatorów

#include "Ulamek.h"
void main()
{

CUlamek ulamekA(1,2),

ulamekB = 3,
ulamekC;

ulamekC = ulamekA + ulamekB;
ulamekC *= ulamekA;
int c;

if(c<=0.2)
{ //...

};

TestUlamek.cpp(22):

error C2676:

Operator binarny '+':

'CUlamek' nie definiuje

takiego operatora lub konwersji

do operatora predefiniowanego.

background image

itm / MVLab (c) 2007-2011

Zły przykład: operatory

#include "Ulamek.h"
// ...
CUlamek Add (CUlamek U2) {
m_iLicznik =

m_iLicznik * U2.m_iLicznik +
m_iMianownik *
U2.m_iLicznik;

m_iMianownik = U2.m_iMianownik;
return *this;
};
CUlamek Mul (CUlamek U2) {
m_iLicznik *= U2.m_iLicznik;
m_iMianownik *= U2.m_iMianownik;
return *this;
};

class CUlamek
{
private:

int m_iLicznik;
int m_iMianownik;

public:

CUlamek(

const CUlamek& kUlamek

);

CUlamek(int iLicznik);
CUlamek(int iLicznik,

int iMianownik);

CUlamek Add (CUlamek U2);
CUlamek Sub (CUlamek U2);
CUlamek Mul (CUlamek U2);
CUlamek Div (CUlamek U2);

};

#include "Ulamek.h"
void main()
{

CUlamek ulamekA(1,2), ulamekB(3), ulamekC;
ulamekC = (ulamekA.Add(ulamekB)).Mul(ulamekC);
// ...

};

Nigdy

w ten

sposób

background image

itm / MVLab (c) 2007-2011

Przeciążanie operatorów –

Przykład '~'

#include "Ulamek.h"
// ...
CUlamek operator~() const {

if(!m_iMianownik) exit(1);
CUlamek tmp(m_iMianownik, m_iLicznik);
return tmp;

};
CUlamek operator*(const CUlamek&

aU) const {

CUlamek tmp(

m_iLicznik*aU.m_iLicznik,
m_iMianownik*aU.m_iMianownik);

return tmp;

};

class CUlamek
{

private:

int m_iLicznik;
int m_iMianownik;

public:

// ...
CUlamek operator~() const;
CUlamek operator*(

const CUlamek& aU)

const;

CUlamek operator=(const Culamek& aU);
// ...

};

#include "Ulamek.h"
void main()
{

CUlamek ulamekA(1,2), ulamekB(3);
Culamek ulamekC = ~ulamekA;

(ulamekA * ulamekB).Wypisz();

};

background image

itm / MVLab (c) 2007-2011

Przeciążanie operatorów

Przykład

#include "Ulamek.h"
// ...
CUlamek operator~() const {

if(!m_iMianownik) exit(1);
CUlamek tmp (m_iLicznik,

m_iMianownik);

return tmp;

};
CUlamek operator*(const CUlamek&

aU) const {

CUlamek tmp(

m_iLicznik*aU.m_iLicznik,
m_iMianownik*

aU.m_iMianownik);

return tmp;

};

Zwracanie wartości 

bez użycia referencji: 
po przetworzeniu obiektu 

przez metodę, 

lokalny obiekt jest niedostępny.
Kompilator zwykle nie dostrzega 
tego problemu.

background image

itm / MVLab (c) 2007-2011

Przeciążanie operatorów 

Przykład '='

#include "Ulamek.h"
// ...
CUlamek& operator=(const CUlamek& aU) 
{ //sprawdzenei konieczne w przypadku

//komponentów dynamicznych
//np. ulamekA = ulamekA
if(this == &aU) 

return *this;

m_iLicznik = aU.m_iLicznik;
m_iMianownik = aU.m_iMianownik;
return *this;

class CUlamek
{
private:

int m_iLicznik;
int m_iMianownik;

public:

// ...
CUlamek operator~() const;
CUlamek operator*(

const CUlamek& aU) 

const;

CUlamek operator=(const CUlamek& aU);
// ...

};

#include "Ulamek.h"
void main() 
{

CUlamek ulamekA(1,2), ulamekB(3), ulamekC;
ulamekC = ulamekA * (~ulamekB);
ulamekC.Wypisz();

};

Obiekt którego 

metoda została wywołana 

(a nie jego kopia!!) 

background image

itm / MVLab (c) 2007-2011

Wzorce klas

l

Używanie funkcji jest zależne od typów danych jakie one

obsługują. Powoduje to m.in. konieczność ich przeciążania.

l

Zasadniczo ta sama funkcjonalność musi być często udostępniana

dla różnych typów danych. Przeciążanie funkcji celem umożliwienia 

współpracy z różnymi typami powoduje:

l

większy nakład programistyczny,

l

trudności w analizie kodu,

l

większe prawdopodobieństwo błędów,

l

problemy z ich znalezieniem w gąszczu podobnych funkcji.

l

Przykładem jest bufor FIFO który działa tak samo dla wszystkich 

możliwych typów danych,

l

Rozwiązaniem jest parametryzacja funkcji i klas pod względem 
typów w postaci tzw. wzorców (szablonów -ang. templates).

→ programowanie generyczne

l

wzorce klas

l

wzorce funkcji

background image

itm / MVLab (c) 2007-2011

Implementacja wzorców

przykład

// Wzorzec funkcji zwracającej większą wartość z dwóch podanych
template<class T> T Max (T wart1, T wart2) {

if(wart1>wart2) return wart1;
else return wart2; }

// ...
CUlamek ulamekA(2,3), ulamekB(3,4), ulamekC;
ulamekC = Max(ulamekA,ulamekB);
int i = Max(4,5);

// Wzorzec klasy CLista
template<class T> class CLista {
public:

Clista();
~Clista();
void dolacz(T);
T podajOstatni(T);
void usunOstatni();
//...

};
Clista<CUlamek> mojaLista;
CUlamek ulamekA(2,3);
mojaLista.dolacz(ulamekA);

Typ T zostanie określony dalej,

podczas wywoływania funkcji;

można podać np. int -wówczas 

zarówno argumenty, jak 

i typ zwracany też zostaną 

zamienione na int

Tu również na tej samej 

zasadzie zadziała typ 

parametryczny T.

mojaLista (typu CLista<CUlamek>)

będzie działała dla typu Clista

-

wszędzie tam,

gdzie w definicji wpisany był typ T.

background image

itm / MVLab (c) 2007-2011

Zasady postępowania z wzorcami

l

Wzorce nie mogą być (pre-)kompilowane oddzielnie.

W tym celu muszą istnieć odpowiednie zależności, 
parametryzowane przez sam wzorzec -

zwykle nie powoduje to błędów.

l

Wzorce muszą być zadeklarowane i zdefiniowane w jednym pliku. 

Są one łączone wg zapotrzebowań. 
Kompilator generuje sprecyzowane wersje klas opartych na wzorcach 

dopiero w przypadku odpowiedniego wywołania.

Ulamek

.h

Ulamek

.cpp

TestUlame

k

.cpp

Kompil

ator

Kompil

ator

Ulamek

.obj

TestUlamek

.obj

Linker

TestUlamek

.exe

Wzorzec

.t

Wzorzec

.t

background image

itm / MVLab (c) 2007-2011

Pamięć dynamiczna

l

Zmienne i tablice są statyczne i nieelastyczne 
-

nie można zmieniać ich rozmiaru.

l

Co zrobić gdy ilość dostępnej pamięci 
jest w czasie programowania nieznana? np.:

l

przetwarzanie tekstu -ile liter?

l

obsługa MP3 -ile piosenek?

l

Rozwiązaniem jest zastosowanie pamięci dynamicznej 

o modyfikowalnym rozmiarze. Jeśli trzeba alokuje się dodatkową 

przestrzeń w pamięci, jeśli nie -można ją zwolnić dla innych potrzeb.

l

Cele:

l

efektywne gospodarowanie pamięcią,

l

elastyczność,

l

dopasowanie do zasobów systemowych.

background image

itm / MVLab (c) 2007-2011

Pamięć dynamiczna w C++

Dlaczego nie tak jak w C?

l

Wady malloc() i free() 

l

wysokie wymagania od programisty

l

niebezpieczeństwo naruszenia zasad ochrony

pamięci

l

brak dopasowania do filozofii obiektowej

l

Rozwiązanie: operatory new() i delete() 

l

dynamiczna obsługa pamięci w C++ za pomocą operatorów new i delete

l

zastosowanie zarówno do typów standardowych jak i do klas

l

automatyczne (niejawne) wywołania konstruktora w przypadku klas

l

operatory new i delete można także przeładowywać w obrębie klasy

l

new i delete istnieją zawsze razem 
-

każdy dynamicznie powołany obiekt musi być w C++ niejawnie usunięty.

background image

itm / MVLab (c) 2007-2011

new i delete -

przykłady

int* piMojaLiczba = new int;
*piMojaLiczba = 4711;
delete piMojaLiczba;

int iRozmiar = 5;
int* piPole = new int [iRozmiar];
for(int j=0; j<iRozmiar; j++) 

piPole[j]=j*11;

delete[] piPole;

CUlamek *pUlamekA = new CUlamek;
CUlamek *pUlamekB = new CUlamek;
CUlamek *pUlamekC = new CUlamek;
pUlamekC = pUlamekB;
//...
delete pUlamekA;
delete pUlamekB;
delete pUlamekC;

4711

piMojaLiczba

44

piPole

33

22

11

0

26

1

L

M

5
8

L

M

0
1

L

M

pUlamekC

pUlamekB

pUlamekA

wywołanie konstruktorów

pUlamekC 

„wisi w powietrzu”

„wysypanie” programu

dynamiczna alokacja tablicy

dynamiczna dealokacja tablicy

(delete bez nawiasów klamrowych 

spowoduje usunięcie wyłącznie 

pierwszego elementu) 

brak konieczności 

konwersji typów 

(typ jest ten sam) 

background image

itm / MVLab (c) 2007-2011

Przykład: 

konstruktor konwertujący

// MyString.h
class CMyString
{
public:

CMyString();
CMyString(const char*);
~CMyString();
void pokaz();

private:

int m_iDlugosc;
char* m_pszBuf;

};

#include "MyString.h"
#include <cstring>
#include <iostream>
using namespace std;
CmyString::CMyString() {

m_iDlugosc = 0;
m_pszBuf = NULL;

};
CMyString::CMyString(const char* pszName {

m_iDlugosc = strlen(pszName);
m_pszBuf = new char[m_iDlugosc+1];
strcpy(m_pszBuf, pszName);

};
CmyString::~CMyString() {

delete[] m_pszBuf;

};
void CmyString::pokaz() {

cout << "STRING-Objekt: " << m_pszBuf;

};

#include "MyString.h"
void main() { CMyString mojString("To jest String");}

m_iDlugosc

m_pszBuf

14

T o

j e s t

S t r i n g

\0

mojString

konstruktor 

korzysta z pamięci 

podręcznej

dealokacja 

w obrębie destruktora

automatyczne 

wywołanie 

destruktora 

na końcu bloku

background image

itm / MVLab (c) 2007-2011

Przykład: 

konstruktor konwertujący

#include "MyString.h"
#include <cstring>
#include <iostream>
using namespace std;
CmyString::CMyString() {

m_iDlugosc = 0;
m_pszBuf = NULL;

};
CMyString::CMyString(const char* pszName {

m_iDlugosc = strlen(pszName);
m_pszBuf = new char[m_iDlugosc+1];
strcpy(m_pszBuf, pszName);

};
CmyString::~CMyString() {

delete[] m_pszBuf;

};
void CmyString::pokaz() {

cout << "STRING-Objekt: " << m_pszBuf;

};

#include "MyString.h"
void main() { CMyString mojString("To jest String");}

UWAGA!

W przypadku

pamięci dynamicznej

ostrożności nigdy za wiele

if(m_pszBuf!=NULL

delete[] m_pszBuf;

m_pszBuf = NULL;

background image

itm / MVLab (c) 2007-2011

Dynamiczne 
komponenty i kopie

Płaskie kopie

l

Standardowo kopiowanie ani przypisanie nie powiela 

zawartości dynamicznej, przez co dynamiczna zawartość 

staje się przynależna do wielu obiektów

l

Ważne w przypadku konstruktorów kopiujących 
oraz operatorów przypisania.

CMyString String1("Pierwszy String");

//Niejawne wyw. konstr. kopującego:
CMyString String2 = String1;
CMyString String3;
//Niejawne wyw.  operat. przypisania:
String3 = String1;
//String4 pozostawiony "w  powietrzu":
CMyString String4("Czwarty String");
String4 = String1;

15

P i e r w s z y

S t r i n g

\
0

15

15

15

String4

String3

String2

String1

C z w a r t y

S t r i n g

\
0

Zamiany dokonane

w którymkolwiek stringu

mają wpływ na inne!

„Wisi

w powietrzu!”

background image

itm / MVLab (c) 2007-2011

Dynamiczne 
komponenty i kopie

Głębokie kopie

l

Dla każdej operacji kopiowania 

zostaje zaalokowana pamięć do której kopiowana jest ta sama zawartość,

jednak w innym, indywidualnie przydzielonym miejscu w pamięci.

CMyString String1("Pierwszy String");

//Niejawne wyw. konstr. kopującego:
CMyString String2 = String1;
CMyString String3;
//Niejawne wyw.  operat. przypisania:
String3 = String1;

//Przygotowanie pamięci dla String4
CMyString String4("Czwarty String");
String4 = String1;

15

P i e r w s z y

S t r i n g \0

15

15

15

String4

String3

String2

String1

C z w a r t y

S t r i n g

\0

Usunięty

P i e r w s z y

S t r i n g \0

P i e r w s z y

S t r i n g \0

P i e r w s z y

S t r i n g \0

background image

itm / MVLab (c) 2007-2011

Przykład: głębokie kopiowanie

własny konstruktor kopiujący i przeciążenie operat.

// MyString.h
class CMyString
{
public:
//...

CmyString(const

CMyString&);

CMyString& operator=

(const CMyString&);

private:

int m_iDlugosc;
char* m_pszBuf;

};

CMyString::CMyString(const CMyString&

argString) 

{

m_iDlugosc = argString.m_iDlugosc;
if(m_iDlugosc == 0) m_pszBuf = NULL;
else {

m_pszBuf = new char[m_iDlugosc+1];
strcpy(m_pszBuf, argString.m_pszBuf);

}

}
CMyString& CMyString::operator= (const

CMyString& argString) 

{  if(this == &argString) return *this;

m_iDlugosc = argString.m_iDlugosc;
if(m_pszBuf != NULLdelete[] m_pszBuf;
if(m_iLaenge == 0) m_pszBuf = NULL;
else {

m_pszBuf = new char[m_iDlugosc+1];
strcpy(m_pszBuf, einString.m_pszBuf);

}
return *this;

}

zabezpieczenie 

przed 

samozniszczeniem

background image

itm / MVLab (c) 2007-2011

Podsumowanie rozdz. 4.3

•Konstruktory

(standardowe, kopiujące,

konwertujące, inne) 

•Destruktory

•Inicjalizacja podczas
• tworzenia instancji
•usuwanie obiektów

•dopasowywanie funkcji

obsługa innych obiektów

•operatory

pozostałe funkcje

Metody

obsługa pamięci

statyczna/dynamiczna

jawne i niejawne

wywołania

przeciążanie

wzorce

przestrzenie

nazw

płytkie

i głębokie

kopiowanie,

głęboki

operator

przypisania

zwracanie wartości

przez referencję

typ metody

(czy zmienia stan?) 

background image

itm / MVLab (c) 2007-2011

Rozróżnianie wskaźników 
i referencji

CUlamek* p_ulamek = &ulamek1;

if(p_ulamek!=NULL)  //p_iPole!=NULL;

delete p_ulamek; //delete[] p_iPole;

p_ulamek=NULL;      //p_iPole=NULL;

5

12

L

M

11

45

L

M

p_ulamek

Ulamek1
0x0011fec1

Ulamek2
0x0011fec9

CUlamek* p_ulamek = new CUlamek;
int *p_iPole = new int[40];

zwykle użycie w C

zwykłe użycie w C++

dowolna pozycja 

znaku wskaźnika

Dynamiczna obsługa pamięci za 

pomocą new i delete

zabezpieczenie przed użyciem 

operatora delete 

na rzecz wskaźnika do NULL

Wskaźnik

zmienna która zawiera adres obszaru pamięci

można wskazywać na: zmienne, obiekty, obszary kodu

możliwa wartość NULL oraz wkaźniki do niesprecyzowanego 

typu

arytmetyka wskaźników (np. dostęp do zmiennych)

background image

itm / MVLab (c) 2007-2011

Rozróżnienie wskaźników 
i referencji

Referencje (nowy element składni C++)

l

Referencje dostarczają odnośników do obiektów

l

Analogicznie do wskaźników: odnośnik do adresu w pamięci

l

W C++ 

każdy obiekt automatycznie dostaje swoją referencję

CUlamek ulamek1(1,2);
//pobranie referencji
CUlamek& aliasulamka = ulamek1;

operator+(const CUlamek& n_ulamek){

this->m_iz=m_iz+n_ulamek.m_iz;

};

0
1

L

M

l

Zastosowanie referencji jest, w porównaniu do wskaźników, pewniejsze, 

ponieważ wskazują zawsze jeden, dokładnie zdefiniowany obiekt,

l

Brak arytmetyki –

referencje nie mogą być np. inkrementowane

l

Mechanizm przekazywania parametrów call-by-reference

l

Dostępne jak „normalne” zmienne

ALE: pomimo takiej samej pisowni 

operator referencji jest łatwo odróżnialny 
od operatora adresu

Op. referencji jest używany zwykle 
w deklaracjach –zaraz za typem danych

Op. adresu używa się przy operacjach 
na zmiennych i stoi zwykle przed ich nazwami

background image

itm / MVLab (c) 2007-2011

Dokładniej o operatorze 
przypisania I

l

Sumowanie atrybutów dwóch obiektów CUlamek;

l

Wynik typu CUlamek;

l

Niejawne wywołanie konstruktora kopiującego;

Dlaczego nie można zastosować zwracania wartości przez referencję?

l

Lokalne kopie obiektów zostaną na końcu działania funkcji usunięte

l

Wobec tego także referencja…

CUlamek ulamek1(4,6);
CUlamek ulamek2(5);
//pobranie referencji
CUlamek suma = ulamek1 + ulamek2;
suma.wyswietl();

CUlamek CUlamek::operator+ (const CUlamek &n_ulamek) {

CUlamek tmp(

(this->m_iLicznik+n_ulamek.m_iLicznik),
(this->m_iMianownik+n_ulamek.m_iMianownik));

return tmp; }

9

7

L

M

4

6

L

M

5

1

L

M

=

+

suma

ulamek1

ulamek2

W przypadku  operatorów 

wartość lewa i prawa 

muszą 

być identyczne

background image

itm / MVLab (c) 2007-2011

Dokładniej o operatorze 
przypisania I

l

Sumowanie atrybutów dwóch obiektów CUlamek;

l

Wynik typu CUlamek;

l

Niejawne wywołanie konstruktora kopiującego;

Dlaczego nie można zastosować zwracania wartości przez referencję?

l

Lokalne kopie obiektów zostaną na końcu działania funkcji usunięte

l

Wobec tego także referencja…

CUlamek ulamek1(4,6);
CUlamek ulamek2(5);
//pobranie referencji
CUlamek suma = ulamek1 + ulamek2;
suma.wyswietl();

CUlamek&

CUlamek::operator+ (const CUlamek &n_ulamek) {

CUlamek tmp(

(this->m_iLicznik+n_ulamek.m_iLicznik),
(this->m_iMianownik+n_ulamek.m_iMianownik));

return tmp; }

9
7

L

M

4
6

L

M

5
1

L

M

=

+

suma

ulamek1

ulamek2

W przypadku  operatorów

wartość lewa i prawa 

muszą być identyczne

background image

itm / MVLab (c) 2007-2011

Dokładniej o operatorze 
przypisania II

l

Obydwa obiekty istnieją – konieczne przypisanie

l

Musi być zgodność typów

l

Przeciążony operator "=" (głębokie kopiowanie)

możliwości implementacji

CUlamek ulamek1(4,6);
CUlamek ulamek2(17,4);

ulamek1 = ulamek2;
ulamek1.wyswietl();

CUlamek

CUlamek::operator= (const CUlamek &n_ulamek) {

if(this==&n_ulamek) //must avoid self destruction
//...
cout<<"assignment operator called";
this->m_iMianownik=n_ulamek.m_iMianownik;
this->m_iLicznik=n_ulamek.m_iLicznik;

CUlamek temp(this->m_iLicznik,this->m_iMianownik);
cout<<"assignment operator finished";
return temp;

}

background image

itm / MVLab (c) 2007-2011

Dokładniej o operatorze 
przypisania II

l

Obydwa obiekty istnieją –konieczne przypisanie

l

Musi być zgodność typów

l

Przeciążony operator "=" (głębokie kopiowanie)

możliwości implementacji

CUlamek ulamek1(4,6);
CUlamek ulamek2(17,4);

ulamek1 = ulamek2;
ulamek1.wyswietl();

CUlamek

CUlamek::operator= (const CUlamek &n_ulamek) {

if(this==&n_ulamek) //must avoid self destruction
//...
cout<<"assignment operator called";
this->m_iMianownik=n_ulamek.m_iMianownik;
this->m_iLicznik=n_ulamek.m_iLicznik;

cout<<"assignment operator finished";

return CUlamek (this->m_iLicznik, this->m_iMianownik);

}

background image

itm / MVLab (c) 2007-2011

Dokładniej o operatorze 
przypisania II

l

Obydwa obiekty istnieją –konieczne przypisanie

l

Musi być zgodność typów

l

Przeciążony operator "=" (głębokie kopiowanie)

możliwości implementacji

CUlamek ulamek1(4,6);
CUlamek ulamek2(17,4);

ulamek1 = ulamek2;
ulamek1.wyswietl();

CUlamek& CUlamek::operator =(const CUlamek& n_Ulamek)
{

if(this == &n_Ulamek) //must avoid self destruction

return *this;

cout << “assignment operator called“;
this->m_iLicznik = n_Ulamek.m_iLicznik;
this->m_iMianownik = n_Ulamek.m_iMianownik;
cout << "\nassignment operator finished\n";

return *this

; // wszystkie kopie zachowane

}

background image

itm / MVLab (c) 2007-2011

Co już było (w rozdz. 3 i 4)?

l

To już potrafimy:

l

implementacja klas niezależnie od ich późniejszego użycia

l

rozdział na deklaracje (header) oraz definicje i użycie 
(implementacja) 

l

stosowanie zasady information-hiding

l

implementacja operacji w postaci
metod i operatorów

l

To musimy sobie przypomnieć:

l

krok 1.: deklaracja
i implementacja klas,

l

krok 2.: zastosowanie
w konkretnym przypadku

l

Teraz chodzi nam o:

l

implementacja zależności pomiędzy klasami.

Rodzic

Potomek 1

Potomek 2

background image

itm / MVLab (c) 2007-2011

itm / MVLab (c) 2007-2008

4.4 

Zależności między klasami

4.4.1 

Relacje współpracy

Kompozycje

Agregacje

4.3.2 Relacje dziedziczenia

Asocjacje

Dziedziczenie proste

Dziedziczenie złożone

background image

itm / MVLab (c) 2007-2011

Relacje współpracy (asocjacje) 

Asocjacja

, to rodzaj zależności 

pomiędzy klasami. 

Opisuje wspólną semantykę i strukturę 

zbioru powiązań między obiektami. 
Instancja asocjacji 

(połączenie między obiektami klasy) 

określa się jako link.

Cechy rozróżniające:

l

wg typu

l

kompozycja

l

agregacja

l

(prosta) asocjacja

l

wg kierunkowości

l

wg wielokrotności

Część zależna

Całość

Część

Całość

1

*

0..1

*

Klasa 2

Klasa 1

-nazwisko
-nr_umowy

Pracownik

-nazwa
-adres

Firma

1

*

-pracodawca -pracobiorca

zatrudniony

-ulica
-kod_poczt

Adres

-

wartość

-adres

Rachunek

*

1

ma

X

background image

itm / MVLab (c) 2007-2011

Kompozycja

Właściwości:

l

Kryterium: zachowanie danych

l

Kapsulacja części składowych

l

Zależność czasu życia

Część zależna

Całość

1

*

// Przykład kompozycji
//Calosc.h
class CCalosc {

private:

int m_wlasciwosc;
class CCzesc {

int m_a;
int m_b;

public:

CCzesc();
void test();

} m_drugaWlasciwosc;

};

// Przykład kompozycji
//Calosc.h
#include "Calosc.h"
CCalosc::CCzesc::CCzesc() {

m_a=0; m_b=0;

};
void CCalosc::CCzesc::test() {

m_a=1; m_b=1;

}

Zagnieżdżone deklaracje 

klas i zmiennych wewnątrz 

klasy zawierającej

background image

itm / MVLab (c) 2007-2011

Agregacja

Właściwości:

l

Kryterim: zachowanie danych

l

Klasy posługują się klasami

l

Zależność składa-się-z…

Część

Całość 2

0..1

*

// Przykład agregacja
// Osoba.h
#include "MyString.h"
class COsoba {

private:

CMyString m_imie;
CMyString m_nazwisko;

public:

//...
};

// Przykład agregacji
// main.cpp
#include "Calosc.h"
void main() {

COsoba Osoba1;
//...

}

Klasa COsoba posługuje się 

obiektami innej klasy 

jako właściwościami.

Całość 1

0..1

background image

itm / MVLab (c) 2007-2011

Osoba1

Agregacja: 

Powoływanie obiektów

// Przykład agregacji
// main.cpp
#include "Calosc.h"
void main() {

COsoba Osoba1;
//...

}

0

NULL

imię

0

NULL

nazwisko

Osoba1

0

NULL

imię

0

NULL

nazwisko

Osoba1

0

NULL

imię

???

???

nazwisko

Osoba1

???

???

imię

???

???

nazwisko

Krok 1:

Alokacja pamięci

Krok 2: Konstruktor 

domyślny dla imię

Krok 3: Konstruktor 

domyślny dla nazwisko

Krok 4: Konstruktor 

domyślny dla klasy 
COsoba

Wywołanie konstruktorów 

domyślnych komponentów 
zagregowanych (w 

kolejności deklaracji) –krok 
2. i 3

.  następuje przed 

wywołaniem konstruktora 
klasy.

background image

itm / MVLab (c) 2007-2011

Lista inicjalizacyjna

l

Klasy bez konstruktorów domyślnych nie mogą być agregowane 

przez inne klasy (z wyjątkiem tych gdzie są zdefiniowane inne 

konstruktory, ale nie domyślny.

l

Wywołanie konstruktora domyślnego elementu agregowanego jest 

bezsensowne, jeśli konstruktor obiektu agregującego nadpisze dane.

// Osoba.h
#include "MyString.h"
Class COsoba {

public:

COsoba(CMyString& imie,

CMyString& nazwisko);

//...

void main() {

CMyString vn = "Marcin";
CMyString nn = "Wróbel";
COsoba ja(vn,nn);
//...

// 

Osoba.cpp (ZŁA WERSJA!)

#include "Osoba.h"
COsoba::COsoba(CMyString& imie,

CMyString& nazwisko) {

m_nazwisko = nazwisko;
m_imie = imie;

};

ja

6

imię

6

nazwisko

Marcin

Wróbel

Krok 4: Konstruktor 

główny nadpisuje ewent. 

wartości wpisane przez 

konstruktor domyślny

.

background image

itm / MVLab (c) 2007-2011

Lista inicjalizacyjna

Właściwości:

l

Pozwalają konstruktorom używanym do inicjalizacji komponentów 

przekazywać argumenty przez wartość

l

Mogą zostać użyte tylko dla konstruktorów

Składnia:

l

Są wypisywane za listą argumentów funkcji, po dwukropku

l

Składają się z listy wyrażeń rozdzielanych przecinkiem, w postaci 
komponent(argument);

// Osoba.cpp (wersja poprawiona)
#include "Osoba.h"
COsoba::COsoba(CMyString& imie, CMyString& nazwisko):

m_imie(imie), m_nazwisko(nazwisko)

{ // Nic więcej nie trzeba robić!
};

Dzięki takiej liście 

nie będą uruchamiane domyślne 

konstruktory  klas zagregowanych,

lecz konstruktory kopiujące.

background image

itm / MVLab (c) 2007-2011

(Proste) Asocjacje

Właściwości:

l

Kryterim: zachowanie danych

l

Obiekty różnych klas „znają się” 
nawzajem

l

Umożliwienie wywołania metod 
obiektów innych klas

l

Cechy: kierunkowość i wielokrotność

Pomocy!! -

Już nie mogę....

l

uwzględnienie wszystkich aspektów 

wyszłoby daleko poza nasze ramy

l

ograniczamy się wyłącznie na 
asocjacjach 1:1, jedno- i 
dwukierunkowych.

-ulica
-kod_poczt

Adres

-

wartość

-adres

Rachunek

*

1

ma

X

Nie umiem 
pływać, nie 
umiem pływać!!!

A ja chodzić, a 
mimo to nie drę 
się jak głupi na 
całą okolicę...

background image

itm / MVLab (c) 2007-2011

Asocjacja -

przykład

Sytuacja:

l

Projektowana jest aplikacja do 

obliczeń arytmetycznych, która 

ma korzystać z obiektów klasy 

ułamek.

l

„Zapoznanie” tych dwóch klas 

ze sobą powinno się odbywać 

w konstruktorze którejś z klas 
programu obliczeniowego.

// Przykład asocjacji
// Arytm.h
#include "Ulamek.h"
class CArytm {

private:

CUlamek* p_ulamek;

public:

CArytm();
CArytm(CUlamek* p_ulamek);

//...

// Przykład asocjacji
// main.cpp
#include "Arytm.h"
void main() {

CUlamek ulamek1(2,3);
CArytm Ar1(&ulamek1);
//...

}

„Zapoznanie”

-licznik
-mianownik

Ulamek

Arytmetyka

0

1

zna

X

Asocjacja przez 

wskaźnik na 

obiekt zewnętrzny

background image

itm / MVLab (c) 2007-2011

Zaprzyjaźnienia (friend)

Wszystkie dotychczas omówione zależności wymagają 

dostępu jednej klasy do metod drugiej. Co jednak, gdy 

nie można tego zrealizować

Przykłady:

l

Operator dodawania zmiennej int i obiektu klasy CUlamek, 

gdy int jest pierwszym operandem wymaga przeciążenia operatora+ dla typu 
int.

l

Wyjście obiektu klasy CUlamek na strumień cout za pomocą standardowego 

operatora << wymaga jego przeciążenia w klasie ostream (z niej pochodzi 
obiekt cout).

Rozwiązanie:

l

Globalne definicje przeładowywanych funkcji jako funkcji zaprzyjaźnionych, 

oznaczonych słowem kluczowym friend, dzięki czemu 

mogą

one mieć dostęp 

do prywatnych składników klas z którymi są zaprzyjaźnione.

ALE:

Każdy kij ma dwa końce! -kolizja z zasadą information-hiding.

CUlamek ulamek1(5,7);
Culamek ulamek2;
int liczba = 3;

ulamek2=liczba+ulamek1;

cout<<"Wynik: "<<ulamek2;

background image

itm / MVLab (c) 2007-2011

Zaprzyjaźnienia - przykład

Sytuacja:

l

chcemy umożliwić operację jak na przykładzie poniżej:

// main.cpp (Funkcje globalne)
ostream& operator<<(ostream& str, const CUlamek& ulamek) {

str << ulamek.m_iLicznik << "/" << ulamek.m_iMianownik;
return str;

}
//...
void main() //...

// CUlamek.h
class Culamek {
private:

int m_iLicznik, m_iMianownik;
friend ostream& operator<<(ostream&, const CUlamek&);

public:

//...

}

Dostęp do prywatnego 

składnika klasy CUlamek.

Globalne przeciążenie operatora <<. 

Zostanie przyjęte przez system 

jeśli nie znajdzie się żaden 

pasujący operator w klasie ostream.

cout << "Wartosc ulamka: "<< ulamek1 << endl;

W ten sposób funkcja 

uzyska dostęp do prywatnych 

składników klasy CUlamek.

background image

itm / MVLab (c) 2007-2011

Dziedziczenie

Dziedziczenie

l

podklasy (także klasy częściowe, klasy wyprowadzone i podklasy) 

dziedziczą wszystkie właściwości i metody swoich klas bazowych.

l

mogą posiadać własne -niezależne od klas bazowych oraz innych 

klas potomnych (rodzeństwa) składniki oraz modyfikować metody 
odziedziczone.

l

zasada ponownego wykorzystania kodu.

Problemy

l

dziedziczenie proste vs

. dziedziczenie złożone

l

polimorfizm vs. funkcje wirtualne.

Rodzic

Potomek

background image

itm / MVLab (c) 2007-2011

Dziedziczenie proste

l

Specjalizacja klasy bazowej

l

Przykład: klasa CUlamekDzies specjalizujący 

klasę CUlamek o możliwość definiowania 

dokładności.

-Licznik: int
-Mianownik: int

Ulamek

-Dokladnosc: int

UlamekDzies

// Przykład dziedziczenia
// UlamekDzies.h
#include "Ulamek.h"
class CUlamekDzies: public CUlamek {

private:

int m_iDokladnosc;

public:

CUlamekDzies(int L, int M, int D);
void pokaz() const;

}

Wyrażenie 

definiujące 

dziedziczenie

Nowy konstruktor 

(trójparametrowy)

Przedefiniowanie 

(dopasowanie) metody

Nowy atrybut

background image

itm / MVLab (c) 2007-2011

Dziedziczenie proste -

przykład

// Przykład dziedziczenia
// UlamekDzies.h
#include "Ulamek.h"
class CUlamekDzies:

public CUlamek

{

private:

int m_iDokladnosc;

public:

CUlamekDzies
(int L, int M, int D);
void pokaz() const;

}

Poniższe przykłady nie należą do dobrego stylu programistycznego

i są przytoczone w tej postaci tylko dla jasności kodu.

// Przykład dziedziczenia
// UlamekDzies.cpp
#include "Ulamek.h"
#include <math.h>
CUlamekDzies::CUlamekDzies

(int iL, int iM, int iD) {

this->Zmien(pow(10,iD)*iL/iM,

pow(10,iD));

m_iDokladnosc=iD;

}
void CUlamekDzies::pokaz() const {

CUlamek::pokaz();
cout<<"Dokladnosc: "<<m_iDokladnosc;

}

#include "UlamekDzies.h"
void main() {

CUlamekDzies mojUlamekDzies(1,3,3);
mojUlamekDzies.pokaz();

}

Odziedziczona metoda

Jawne wywołanie 

metody klasy zewnętrznej

background image

itm / MVLab (c) 2007-2011

Polimorfizm

l

dzięki polimorfizmowi możliwe jest wywoływanie metody obiektu bez 
odgórnej wiedzy jakiej jest on klasy -

system sam ją określi i 

spowoduje że na pewno zareaguje on na swój specyficzny sposób

l

dziedziczenie powoduje zachowania polimorficzne

#include "UlamekDzies.h"
void main() {

CUlamekDzies mojUlamekDzies(1,3,3);
CUlamek mojUlamek(2,5);
CUlamek *wynikUlamkowy;

//Przypadek 1

wynikUlamkowy = &mojUlamek;
wynikUlamkowy->pokaz();

//Przypadek 2

wynikUlamkowy = &mojUlamekDzies;

wynikUlamkowy->pokaz();

}

Niestety 

nie pokaże tego 

co chcemy

OK.

CUlamekDzies 

jest przecież 

również CUlamek

background image

itm / MVLab (c) 2007-2011

Polimorfizm i wirtualność

// Ulamek.h
#include <iostream>
using namespace std;

class CUlamek {

private:

int m_iLicznik;
int m_iMianownik;

public:

virtual void pokaz() const;
virtual ~CUlamek();

//...
};

#include "UlamekDzies.h"
void main() {

CUlamekDzies

mojUlamekDzies(1,3,3);

CUlamek mojUlamek(2,5);
CUlamek *wynikUlamkowy;

//Przypadek 1

wynikUlamkowy = &mojUlamek;
wynikUlamkowy->pokaz();

//Przypadek 2

wynikUlamkowy =

&mojUlamekDzies;

wynikUlamkowy->pokaz();

}

Słowo kluczowe virtual w pliku nagłówkowym 

mówi o tym, że metoda klasy potomnej 

może zostać nadpisana.

Szczególnie ważne: wirtualny destruktor!!

Dopasowanie metod w czasie działania

przez tzw. wirtualną tablicę metod.

Zalety: zachowuje się tak jak chcemy

Wady: utrata efektywności.

background image

itm / MVLab (c) 2007-2011

Przykład zastosowania: 
Zdarzenia

+ ~DostZdarz()

DostawcaZdarzenia

+ DostZdarz(in Handl1: HandlZdarz&)

-Handler: HandlZdarz

DostawcaZdarzenia

+ HandlZdarz()
+ nowyAbonent(in Abnt: AbonentZdarz&)
+ usunAbonenta(in Abnt: AbonentZdarz&)
+ odklejOdZdarz(in Dost: DostZdarz&)

-Ilosc: int
-Abonamenty: wektor<AbonentZdarz*>

HandlerZdarzenia

DataChangedHandler

+ OnZdarzenie(in Dost: DostZdarz&): bool

AbonentZdarzenia

+ OnZdarzenie(in Dost: DostZdarz&): bool

Wizualizacja

background image

itm / MVLab (c) 2007-2011

Powtórka i podsumowanie 
rozdz. 4.4

•Kompozycje
•Agregacje
•Asocjacje

Klasy używają klas

Zależność „has-a”

Inne zależności

Zależność „is-a”

•Dopasowywanie i 
rozszerzanie dziedziczonej 

funkcjonalności

•Dziedziczenie proste
•(Dziedziczenie 
wielokrotne)

Zależności między klasami

Zaprzyjaźnienia

(friends)

listy inicjalizacyjne

polimorfizm

wirtualność

background image

itm / MVLab (c) 2007-2011

Czego zabrakło - rozdział 4.5

4.1 

Zanim rozpoczniemy implementację...

4.2 Pierwsze kroki w kierunku

programowania w C++

4.3 

Klasy „rosną”...

4.4 

Zależności pomiędzy klasami

4.5 

Implementacja rozwiązań

(w następnym semestrze)

Kiedy przećwiczymy podstawy

będziemy programować 

pierwsze aplikacje obiektpwe w c++


Document Outline