POwyklad7

background image

Tomasz Marks - Wydział MiNI PW

-1-

Programowanie Obiektowe

(j

ę

zyk C++)

Wykład 7.

Tomasz Marks - Wydział MiNI PW

-2-

Problem projektowy

DANE WEJ

Ś

CIOWE:

Dysponujemy dobrze zdefiniowan

ą

i przetestowan

ą

klas

ą

Vector,

która faktycznie pozwala na operowanie dynamicznie alokowanymi
tablicami danych liczbowych.

ZADANIE:

Chcemy operowa

ć

wektorami, które zwi

ą

zane b

ę

d

ą

z pewnymi

zadawanymi dowolnie jednostkami fizycznymi ( n.p. [m], [m/s],
[m/s2], [A], [V] ).
Dla uproszczenia zało

ż

ymy,

ż

e wszystkie współrz

ę

dne wektora

zwi

ą

zane s

ą

z t

ą

sam

ą

jednostk

ą

.

Klas

ę

nazwiemy PhVectorX

Prze

ś

ledzimy dwa sposoby podej

ś

cia do tego problemu definiuj

ą

c

dwie klasy: PhVectorA i

PhVectorB.

Tomasz Marks - Wydział MiNI PW

-3-

KLASY

POCHODNE

DZIEDZICZENIE

Tomasz Marks - Wydział MiNI PW

-4-

Klasa pochodna klasy Vector [1]

// phvectorb.h

#include "vector.h"
#define U_LEN 10

class PhVectorB

: public Vector

{

char Unit [ U_LEN ];

public:

PhVectorB ( int = 0, double* = 0, char* = "" );
PhVectorB ( const PhVectorB& );
~PhVectorB ( );

PhVectorB& operator = ( const PhVectorB& );
void setUnit ( const char* );
const char* getUnit ( ) const;
....................................

friend istream& operator >> ( istream&, PhVectorB& );
friend ostream& operator << ( ostream&, const PhVectorB& );

};

background image

Tomasz Marks - Wydział MiNI PW

-5-

Klasa pochodna klasy Vector [2]

Powiemy,

ż

e ….

Klasa PhVectorB

dziedziczy

(dokładniej: dziedziczy publicznie)

klas

ę

Vector.

… dziedziczy klas

ę

… dziedziczy z klasy …
… dziedziczy po klasie …

Klasa Vector jest klas

ą

bazow

ą

(dokładniej: publiczn

ą

klas

ą

bazow

ą

).

Klasa PhVectorB jest klas

ą

pochodn

ą

dziedzicz

ą

c

ą

z klasy Vector.

UWAGA: W j

ę

zyku C++ dopuszczalne jest równie

ż

dziedziczenie

wielobazowe (tzn. dziedziczenie jednocze

ś

nie z wielu

klas bazowych).

Tomasz Marks - Wydział MiNI PW

-6-

Klasa pochodna klasy Vector [3]

1. Klasa PhVectorB (pochodna) zawiera nienazwan

ą

składow

ą

klasy Vector.

2. W tre

ś

ci metod klasy pochodnej wolno odwoływa

ć

si

ę

jawnie

do publicznych (public) i chronionych (protected) składowych
klasy bazowej.

3. Lista inicjacyjna konstruktora pochodnej nie mo

ż

e zawiera

ć

bezpo

ś

rednich inicjatorów składowych bazowej.

Trzeba tu u

ż

y

ć

inicjatora bazowej w postaci jawnego wywołania

jej konstruktora.

4. Ka

ż

dy obiekt klasy pochodnej jest obiektem klasy bazowej.

Ale nie odwrotnie !

5. Ka

ż

da metoda klasy bazowej mo

ż

e by

ć

wywołana na rzecz obiektu

klasy pochodnej (dziedziczenie metod). Nie dotyczy to konstruktorów,
destruktora i operatora przypisania.

Tomasz Marks - Wydział MiNI PW

-7-

Klasa pochodna klasy Vector [4]

Ka

ż

dy PhVectorB jest Vector'em

(uzupełnionym dodatkowymi informacjami).

W konsekwencji:

Vector V, *pV;
PhVectorB PV, *pPV;

V = PV;

// o.k. - przepisz na V cz

ęść

PV opisuj

ą

c

ą

Vector

// (slicing – wycinanie)

Vector W = PV;

// o.k. - inicjuj W cz

ęś

ci

ą

PV opisan

ę

Vector'em

PV = V;

//

Ą

D

- co zrobi

ć

z cz

ęś

ci

ą

PV nieopisan

ą

Vector'em?

pV = &PV;

// o.k. - PV jest Vector'em.

pPV = &V;

//

Ą

D

- V nie jest typu PhVectorB,

// jaki jest sens pPV->Unit po takim przypisaniu

Vector & rV = PV;

// o.k. - referencja do składowej typu Vector w obiekcie PV

PhVectorB & rPV = V;

//

Ą

D

- co oznacza rPV.Unit po takim skojarzeniu?

Tomasz Marks - Wydział MiNI PW

-8-

Klasa pochodna klasy Vector [5]

Ka

ż

dy PhVectorB jest Vector'em

(uzupełnionym dodatkowymi informacjami).

Kompilator niejawnie stosuje konwersje:

Vector V, *pV;
PhVectorB PV, *pPV;

V = PV;

// o.k. V = (Vector) PV

Vector W = PV;

// o.k. Vector W = (Vector) PV;

PV = V;

//

Ą

D

pV = &PV;

// o.k. pV = (Vector*) &PV;

pPV = &V;

//

Ą

D

Vector & rV = PV;

// o.k. Vector& rV = (Vector&) PV;

PhVectorB & rPV = V;

//

Ą

D

background image

Tomasz Marks - Wydział MiNI PW

-9-

PhVectorB – implementacje [1]

// phvectorb.cpp

#include <cstring>

// ew. <string.h> jak w j

ę

z. C

// niektóre kompilatory pozwalaj

ą

pomija

ć

// pliki nagłówkowe 'odziedziczone' z C

#include "phvectorb.h"

const char* PhVectorB::getUnit ( ) const
{ return Unit; }

void PhVectorB::setUnit ( const char* str )
{ strncpy ( Unit, str, U_LEN ); }

………………………………………………………...
…………………………………………………………

Tomasz Marks - Wydział MiNI PW

-10-

PhVectorB – implementacje [2]

PhVectorB::PhVectorB ( int s, double tab[ ], char* u )

{

// odziedziczona składowa klasy Vector ju

ż

istnieje

// zainicjowana wywołaniem konstruktora Vector ( )

strncpy ( Unit, u, U_LEN );

// jak "przewymiarowa

ć

" składow

ą

odziedziczon

ą

, by miała

// rozmiar s i była zapełniona warto

ś

ciami z tablicy tab ???

………………………………………………………...

…………………………………………………………

Tomasz Marks - Wydział MiNI PW

-11-

PhVectorB – implementacje [3]

PhVectorB::PhVectorB ( int s, double tab[ ], char* u )

: Vector ( s, tab )

{

// odziedziczona składowa klasy Vector ju

ż

istnieje

// zainicjowana wywołaniem konstruktora

Vector ( s, tab )

strncpy ( Unit, u, U_LEN );

// i nic wi

ę

cej nie trzeba robi

ć

!!!

}

Tomasz Marks - Wydział MiNI PW

-12-

PhVectorB – implementacje [4]

PhVectorB::PhVectorB ( const PhVectorB& arg )

: Vector ( arg )

{

// odziedziczona składowa klasy Vector ju

ż

istnieje

// zainicjowana wywołaniem konstruktora

Vector ( arg )

strncpy ( Unit, arg.Unit, U_LEN );

// i nic wi

ę

cej nie trzeba robi

ć

!!!

}

Ale

UWAGA:

Niemal dokładnie to samo zrobi predefiniowany

konstruktor kopiuj

ą

cy, realizuj

ą

cy kopiowanie płytkie: dla pól

obiektowych zostan

ą

u

ż

yte odpowiednie konstruktory kopiuj

ą

ce,

a dla pozostałych (nieobiektowych i tablicowych) wykonane b

ę

dzie

proste kopiowanie zawarto

ś

ci pami

ę

ci.

WNIOSEK:

Definicj

ę

(i deklaracj

ę

) tego konstruktora mo

ż

na (nale

ż

y)

w klasie PhVectorB pomin

ąć

.

background image

Tomasz Marks - Wydział MiNI PW

-13-

PhVectorB – implementacje [5]

W wersji

PhVectorB::PhVectorB ( const PhVectorB& arg )

: Vector ( arg )

{ .... }

kompilator zastosuje konwersj

ę

PhVectorB::PhVectorB ( const PhVectorB& arg )

: Vector ( (Vector&)arg )

{ .... }

Zauwa

ż

my te

ż

,

ż

e gdyby

ś

my si

ę

zdecydowali pisa

ć

własn

ą

wersj

ę

konstruktora kopiuj

ą

cego, to mogliby

ś

my alternatywnie u

ż

y

ć

innego

konstruktora dla inicjalizacji składowej dziedziczonej (zakładaj

ą

c

dost

ę

pno

ść

pól Size i V ):

PhVectorB::PhVectorB ( const PhVectorB& arg )

: Vector ( arg.Size, arg.V )

{ .... }

Tomasz Marks - Wydział MiNI PW

-14-

Tworzenie / niszczenie obiektów [2]

Etapy tworzenia obiektu:

1. Przydział pami

ę

ci dla niestatycznych pól obiektu,

w tym składowej bazowej

.

2. Wykonanie inicjalizacji w oparciu o pełn

ą

list

ę

inicjacyjn

ą

z uwzgl

ę

dnieniem inicjatora składowej bazowej

.

3. Wykonanie tre

ś

ci

konstruktor

a.

Etapy niszczenia obiektu:

1. Wykonanie tre

ś

ci

destruktor

a.

2. Wykonanie tre

ś

ci destruktorów pól składowych,

z uwzgl

ę

dnieniem destruktora składowej bazowej

.

3. Zwolnienie pami

ę

ci przydzielonej dla niestatycznych pól obiektu,

w tym składowej bazowej

.

Tomasz Marks - Wydział MiNI PW

-15-

PhVectorB – implementacje [6]

PhVectorB::~PhVectorB ( )

{

// nic nie trzeba robi

ć

!!!

}

UWAGA 1.:

Je

ż

eli destruktor został zadeklarowany, to musimy

zdefiniowa

ć

jego implementacj

ę

.

UWAGA 2.:

Dokładnie to samo nic zrobiłby destruktor predefiniowany.

WNIOSEK:

Definicj

ę

(i deklaracj

ę

) destruktora mo

ż

na (nale

ż

y)

w klasie PhVectorB pomin

ąć

.

Tomasz Marks - Wydział MiNI PW

-16-

PhVectorB – implementacje [7]

PhVectorB& PhVectorB::operator = ( const PhVectorB& rhs )

{

(Vector&)*this = (Vector&) rhs;

// operacja w klasie Vector

strcpy ( Unit, rhs.Unit );

return *this;

}

Ale

UWAGA:

Niemal dokładnie to samo zrobi predefiniowany

operator przypisania, realizuj

ą

cy kopiowanie płytkie: dla pól

obiektowych zostan

ą

u

ż

yte odpowiednie operatory przypisania,

a dla pozostałych (nieobiektowych i tablicowych) wykonane b

ę

dzie

proste kopiowanie zawarto

ś

ci pami

ę

ci.

WNIOSEK:

Definicj

ę

(i deklaracj

ę

) tego operatora mo

ż

na (nale

ż

y)

w klasie PhVectorB pomin

ąć

.

background image

Tomasz Marks - Wydział MiNI PW

-17-

PhVectorB – implementacje [8]

Implementacje strumieniowych operatorów WE/WY mog

ą

wygl

ą

da

ć

jak ni

ż

ej:

istream& operator >> ( istream& inp, PhVectorB& vec )
{

inp >> (Vector&) vec;

// konieczna jawna konwersja !!!

inp >> vec.Unit;
return inp;

}

ostream& operator << ( ostream& out, const PhVectorB& vec )
{

out << (Vector&) vec;

// konieczna jawna konwersja !!!

out << "[" << vec.Unit << "]";
return out;

}

Tomasz Marks - Wydział MiNI PW

-18-

PhVectorB – u

ż

ycie [1]

// phvb_prog.cpp

#include "phvectorb.h"

double forA [ ] = { 1., 2., 3., };
double forB [ ] = { 4., 5., 6., };

int main ( )
{

PhVectorB A ( 3, forA, "m/s" ), B ( 3, forB, "m/s" ), C;
Vector D;
double a, b; int i, j;
char txt [ 20 ];

strcpy ( txt, A.getUnit ( ) );

// o.k.

j = B.W.getSize ( );

//

Ą

D

(brak pola W w obiekcie B)

i = B.getSize ( );

// O.K. ! ( getSize – odziedziczona metoda )

b = A.W [ 2 ];

//

Ą

D

(brak pola W w obiekcie B)

a = A [ 2 ];

// O.K. ! ( [ ] – odziedziczona metoda)

C.setUnit ( "m/s2" );

// o.k.

C = A + B;

//

Ą

D

A + B;

// O.K. ! (wynik typu Vector)

D = A + B;

// o.k.

Tomasz Marks - Wydział MiNI PW

-19-

PhVectorB – u

ż

ycie [2]

WNIOSEK:

Metody takie jak

getSize()

(pobranie rozmiaru),

[ ]

(indeksowanie),

s

ą

dost

ę

pne dla obiektów klasy PhVectorB, bez jawnej kwalifikacji

nazw

ą

pola klasy Vector (tej nazwy faktycznie nie ma).

Te składowe s

ą

odziedziczone z klasy Vector.

Operator

+ (dodawanie),

mo

ż

e by

ć

zastosowany do argumentów typu PhVectorB, ale jego

wynikiem jest obiekt typu Vector. Aby dysponowa

ć

dodawaniem

zwracaj

ą

cym typ PhVectorB, trzeba przeci

ąż

y

ć

go dla obiektów

tej klasy.

Tomasz Marks - Wydział MiNI PW

-20-

Klasa pochodna klasy Vector [6]

// phvectorb.h

#include "vector.h"
#define U_LEN 10

class PhVectorB : public Vector
{

char Unit [ U_LEN ];

public:

PhVectorB ( int = 0, double* = 0, char* = "" );

void setUnit ( const char* );
const char* getUnit ( ) const;
....................................

friend PhVectorB operator + (const PhVectorB&, const PhVectorB& );

friend istream& operator >> ( istream&, PhVectorB& );
friend ostream& operator << ( ostream&, const PhVectorB& );

};

background image

Tomasz Marks - Wydział MiNI PW

-21-

PhVectorB – implementacje [9]

// funkcja zaprzyja

ź

niona

PhVectorB operator+ ( const PhVectorB& a, const PhVectorB& b )

{

PhVectorB c;

(Vector&) c = (Vector&) a + (Vector&) b;

// operacja w klasie Vector

c.setUnit( a.Unit );

return c;

}

Tomasz Marks - Wydział MiNI PW

-22-

PhVectorB – implementacje [9a]

// funkcja zaprzyja

ź

niona (inny wariant)

PhVectorB operator+ ( const PhVectorB& a, const PhVectorB& b )

{

PhVectorB sum ( ( a.getSize() > b.getSize() )

? a.getSize() : b.getSize(), 0, (char*) a.Unit );

for ( int i = 0; i < sum. getSize(); ++i )

{

if ( i < a.getSize() ) sum[ i ] = a.getAt( i );

if ( i < b.getSize() ) sum[ i ] += b.getAt( i );

}

return sum;

}

Tomasz Marks - Wydział MiNI PW

-23-

PhVectorB – implementacje [...]

W tym wariancie nie ma bezpo

ś

rednich odwoła

ń

do składowych

odziedziczonych z klasy Vector, poniewa

ż

s

ą

one w niej prywatne.

ś

eby mo

ż

na było napisa

ć

pro

ś

ciej ten wariant dodawania,

trzeba zmodyfikowa

ć

definicj

ę

klasy bazowej:

class Vector
{

public:

int Size;
double *V;

private:

static int DefSize;
void Init ( int size=0, double val=0, double *arr=0 );

public:

Vector ( );
……………..
……………..

};

Tomasz Marks - Wydział MiNI PW

-24-

PhVectorB – implementacje [9b]

Teraz poprawna b

ę

dzie implementacja w postaci:

// funkcja zaprzyja

ź

niona (jeszcze inny wariant)

PhVectorB operator+ ( const PhVectorB& a, const PhVectorB& b )

{

Vector sum ( ( a.Size > b.Size ) ? a.Size : b.Size );

for ( int i = 0; i < sum. Size; ++i )

{

if ( i < a.Size ) sum.V[ i ] = a.V[ i ];

if ( i < b.Size ) sum.V[ i ] += b.V[ i ];

}

return PhVectorB ( sum.Size, sum.V, (char*)a.Unit );

}

background image

Tomasz Marks - Wydział MiNI PW

-25-

PhVectorB – implementacje [...]

Faktycznie najsensowniejsze byłoby mie

ć

klas

ę

Vector zdefiniowan

ą

z u

ż

yciem składowych chronionych:

class Vector

{

protected:

int Size;

double *V;

private:

static int DefSize;

void Init ( int size=0, double val=0, double *arr=0 );

public:

Vector ( );

……………..

……………..

};

Tomasz Marks - Wydział MiNI PW

-26-

PhVectorB – implementacje [9d]

Mo

ż

na temu zaradzi

ć

deklaruj

ą

c sum, jako zmienn

ą

typu PhVectorB:

// funkcja zaprzyja

ź

niona (jeszcze inny wariant)

PhVectorB operator+ ( const PhVectorB& a, const PhVectorB& b )

{

PhVectorB

sum ( (a.Size>b.Size)?a.Size:b.Size, 0, (char*)a.Unit );

for ( int i = 0; i < sum. Size; ++i )

{

if ( i < a.Size ) sum.V[ i ] = a.V[ i ];

if ( i < b.Size ) sum.V[ i ] += b.V[ i ];

}

return sum;

}

Tomasz Marks - Wydział MiNI PW

-27-

METODY

WIRTUALNE

Tomasz Marks - Wydział MiNI PW

-28-

Metody wirtualne – wprowadzenie [1]

W klasie Vector zadeklarowali

ś

my metod

ę

Norm()

class Vector
{

……………..

public:

double Norm ( ) const;

……………..

};

o implementacji

// norma euklidesowa

double Vector :: Norm ( ) const
{

double s = 0;
for ( int i = 0; i < Size; ++i ) s += V[ i ]*V[ i ];
return sqrt( s );

}

background image

Tomasz Marks - Wydział MiNI PW

-29-

Metody wirtualne – wprowadzenie [2]

Je

ż

eli w klasie pochodnej PhVectorB chcemy przedefiniowa

ć

metod

ę

Norm(),

to jest to dozwolone:

class PhVectorB : public Vector
{

……………..

public:

double Norm ( ) const;

……………..

};

o implementacji

// norma taksówkowa

double PhVectorB :: Norm ( ) const
{

double s = 0;
for ( int i = 0; i < Size; ++i ) s += fabs( V[ i ] );
return s;

}

Tomasz Marks - Wydział MiNI PW

-30-

Metody wirtualne – wprowadzenie [3]

Rozpatrzmy teraz fragment programu:

Vector V( 4, 1.0 );
PhVectorB P ( 4 ); P[0] = P[1] = P[2] = P[3] = 1;

cout << V.Norm();

// 2

cout << P.Norm();

// 4 //norma taksówkowa 'zasłoniła' norm

ę

euklidesow

ą

Vector* pV = &V;
PhVectorB* pP = &P;

cout << pV->Norm();

// 2

cout << pP->Norm();

// 4

cout << pP->Vector::Norm();

// 2

pV = &P;

cout << pV->Norm();

// 2

I to jest zgodne z oczekiwaniem, poniewa

ż

pV jest typu Vector*,

wi

ę

c "z natury" uruchamia metody nale

żą

ce do klasy Vector.

cout << pV->getUnit();

//

Ą

D !

Tomasz Marks - Wydział MiNI PW

-31-

Metody wirtualne [1]

W klasie Vector zadeklarujemy metod

ę

Norm() jako metod

ę

wirtualn

ą

.

class Vector
{

……………..

public:

virtual

double Norm ( ) const;

……………..

};

W implementacji nic si

ę

nie zmienia (virtual zakazane):

// norma euklidesowa

double Vector :: Norm ( ) const
{

double s = 0;
for ( int i = 0; i < Size; ++i ) s += V[ i ]*V[ i ];
return sqrt( s );

}

Tomasz Marks - Wydział MiNI PW

-32-

Metody wirtualne [2]

W klasie pochodnej PhVectorB mo

ż

emy, ale nie musimy, powieli

ć

specyfikator virtual:

class PhVectorB : public Vector
{

……………..

public:

virtual

double Norm ( ) const;

……………..

};

W implementacji nic si

ę

nie zmienia (virtual zakazane):

// norma taksówkowa

double PhVectorB :: Norm ( ) const
{

double s = 0;
for ( int i = 0; i < Size; ++i ) s += fabs( V[ i ] );
return s;

}

background image

Tomasz Marks - Wydział MiNI PW

-33-

Metody wirtualne [3]

Rozpatrzmy jeszcze raz ten sam fragment programu co poprzednio:

Vector V( 4, 1.0 );
PhVectorB P ( 4 ); P[0] = P[1] = P[2] = P[3] = 1;

cout << V.Norm();

// 2

cout << P.Norm();

// 4 //norma taksówkowa 'zasłoniła' norm

ę

euklidesow

ą

Vector* pV = &V;
PhVectorB* pP = &P;

cout << pV->Norm();

// 2

cout << pP->Norm();

// 4

cout << pP->Vector::Norm();

// 2

pV = &P;

cout << pV->Norm();

// 4

//norma taksówkowa z klasy PhVectorB

cout << pV->Vector::Norm();

// 2 //norma euklidesowa z klasy Vector

Tym razem wska

ź

nik pV, który jest typu Vector*, po zainicjowaniu wskazaniem

na obiekt klasy pochodnej uruchamia wersj

ę

metody zdefiniowan

ą

w klasie,

do której nale

ż

y obiekt.

Tomasz Marks - Wydział MiNI PW

-34-

Metody wirtualne [4]

Podobnie b

ę

dzie dla referencji:

Vector V( 4, 1.0 );
PhVectorB P ( 4 ); P[0] = P[1] = P[2] = P[3] = 1;

cout << V.Norm();

// 2

cout << P.Norm();

// 4 //norma taksówkowa 'zasłoniła' norm

ę

euklidesow

ą

Vector& rV = V;
PhVectorB& rP = P;

cout << rV.Norm();

// 2

cout << rP.Norm();

// 4

cout << rP.Vector::Norm();

// 2

Vector& rV2 = P;

cout << rV2.Norm();

// 4

//norma taksówkowa z klasy PhVectorB

cout << rV2.Vector::Norm();

// 2 //norma euklidesowa z klasy Vector

Referencja rV2, która jest typu Vector&, po zainicjowaniu obiektem
klasy pochodnej uruchamia wersj

ę

metody zdefiniowan

ą

w klasie,

do której nale

ż

y obiekt.

Tomasz Marks - Wydział MiNI PW

-35-

Koniec wykładu 7.


Wyszukiwarka

Podobne podstrony:
POwyklad2
POwyklad9
POwyklad3
POwyklad5
POwyklad10
Pedagogika przewlekle chorych powyklejane, Ważne dla sudenta, Studia pedagogika
POwyklad9
POwyklad2
POwyklad1
POwyklad6
POwyklad4
POwyklad15 planowany
POwyklad4
POwyklad3
POwyklad15 planowany
POwyklad1
POwyklad6
POwyklad2
POwyklad9

więcej podobnych podstron