cpp 2

background image

Programowanie C++

1

Klasa

Klasa w C++ jest structurą zawierającą pewne dane składowe

oraz pewne funkcje składowe. Funkcje składowe działają na

danych składowych. Niektóre składniki są public, inne są private.

Klasa w C++ jest structurą zawierającą pewne dane składowe

oraz pewne funkcje składowe. Funkcje składowe działają na

danych składowych. Niektóre składniki są public, inne są private.

class Point {
private:
double x;
double y;
public:
Point();
Point(double x, double y);
~Point();
double X() const;
double Y() const;
void Set(double newX, double newY);
void Move(double deltaX, double deltaY);
double DistanceTo(const Point& other) const;
void Display() const;
};

class Point {
private:
double x;
double y;
public:
Point();
Point(double x, double y);
~Point();
double X() const;
double Y() const;
void Set(double newX, double newY);
void Move(double deltaX, double deltaY);
double DistanceTo(const Point& other) const;
void Display() const;
};

Przykład

:

dane składowe

funkcje składowe

Dane składowe powinny być

prywatne,
funkcje najczęściej są publiczne

background image

Programowanie C++

2

Klasa jest typem danych

Zatem klasa Point jest używana do deklarowania obiektów:

Zatem klasa Point jest używana do deklarowania obiektów:

Point p1;
Point p2;
Point a[10];

Point p1;
Point p2;
Point a[10];

Z obiektami klasy Point możemy używać funkcji składowych tej

klasy . Zwróć uwagę na syntaks:

Z obiektami klasy Point możemy używać funkcji składowych tej

klasy . Zwróć uwagę na syntaks:

p1.Set(3.0, 4.0);
double x = p1.DistanceTo(p2);
a[2].Move(2.0, -2.5);
a[0].Display();
a[9].Set(a[1].X(), 0.0);

p1.Set(3.0, 4.0);
double x = p1.DistanceTo(p2);
a[2].Move(2.0, -2.5);
a[0].Display();
a[9].Set(a[1].X(), 0.0);

Jeden

punkt

Inny punkt

Tablica 10-ciu punktów

background image

Programowanie C++

3

Funkcje posiadają jeden obiekt

Wszystkie funkcje klasy Point działają na obiekcie klasy

Point. Ten obiekt jest podmiotem funkcji

Wszystkie funkcje klasy Point działają na obiekcie klasy

Point. Ten obiekt jest podmiotem funkcji

p1.Set(3.0, 4.0);

double x = p1.DistanceTo(p2);

a[2].Move(2.0, -2.5);

a[0].Display();

a[9].Set(a[1].X(), 0.0);

p1.Set(3.0, 4.0);

double x = p1.DistanceTo(p2);

a[2].Move(2.0, -2.5);

a[0].Display();

a[9].Set(a[1].X(), 0.0);

p1 jest obiektem; 3.0 i 4.0 są

argumentami. Wartość p1 ulegnie

zmianie.

p1 jest obiektem-podmiotem; p2

jest obiektem- argumentem.

Wyliczana jest odległość pomiędzy

p1 i p2.

a[2] jest podmiotem. 2.0 i -2.5 są

argumentami. Wartość a[2] zmienia

się.

a[0] jest podmiotem; funkcja Display nie

ma argumentów. Wartość a[0] jest

wyświetlana.

a[9] podmiotem; a[1].X() i 0.0 są

argumentami. W wywołaniu funkcji

a[1].X(), a[1] jest podmiotem; funkcja X nie

posiada argumentów.

Obiektem(podmiotem) funkcji jest ten obiekt, na rzecz którego funkcja jest

wywoływana i który podlega jej działaniu.

Obiektem(podmiotem) funkcji jest ten obiekt, na rzecz którego funkcja jest

wywoływana i który podlega jej działaniu.

background image

Programowanie C++

4

Konstruktory

Każda klasa ma jeden lub więcej konstruktorów. Konstruktory

mają taką samą nazwę jak klasa. Konstruktor jest wywoływany

zawsze wtedy, gdy tworzony jest obiekt danej klasy. Konstruktor

jest funkcją, która dba o to, by obiekt został utworzony

poprawnie.

Klasa Point posiada dwa konstruktory:

class Point {
...
Point();
Point(double x, double y);
...
};

class Point {
...
Point();
Point(double x, double y);
...
};

Konstruktor domyślny

W tym przypadku zakłada się, wydaje się rozsądne przypuszczenie, że

konstruktor domyślny ustawia obie dane składowe na 0.0, podczas gdy

drugi przypisuje im wartości przekazane przez parametry.

background image

Programowanie C++

5

Destruktory

Każda klasa posiada dokładnie jeden destruktor. Destruktor jest

wywoływany automatycznie w chwili gdy obiekt przestaje istnieć.

Destruktor klasy X posiada nazwę ~X.

class Point {
...
~Point();
...
};

class Point {
...
~Point();
...
};

Zwykle destruktory są używane w sytuacji gdy chcemy odzyskać

pamięć, która była przydzielona dynamicznie a obiekt, który jej

używał nie będzie więcej potrzebny.

W prostych klasach takich jak Point, które nie rezerwują pamięci

dynamicznie destruktory nic nie robią.

background image

Programowanie C++

6

Selektory

Selektory są funkcjami składowymi, które zwracają informację na

temat stanu obiektu, ale go nie zmieniają.

class Point {
...
double X() const;
double Y() const;
double DistanceTo(const Point& other) const;
void Display() const;
};

class Point {
...
double X() const;
double Y() const;
double DistanceTo(const Point& other) const;
void Display() const;
};

Nazwy tych funkcji sugerują ich znaczenie: funkcja X zwraca wartość

współrzędnej x; funkcja Y zwraca wartość współrzędnej y; funkcja

DistanceTo oblicza odległość od punktu do innego punktu; funkcja Display

wypisuje wartości współrzędnych na ekranie monitora.

Będziemy programowali selektory jako funkcje const.

background image

Programowanie C++

7

Modyfikatory

Modyfikatory są funkcjami, które zmieniają stan obiektu i nic nie

zwracają

class Point {
...
void Set(double newX, double newY);
void Move(double deltaX, double deltaY);
...
};

class Point {
...
void Set(double newX, double newY);
void Move(double deltaX, double deltaY);
...
};

Funkcja Set przypisuje nowe wartości współrzędnym; funkcja Move

dodaje przesunięcie do współrzędnych.

Funkcje mogą być równocześnie selektorami i modyfikatorami, czyli

funkcjami zwracającymi stan obiektu i równocześnie zmieniającymi go.

Klasy do tego wykładu nie będą zawierały takich funkcji.

background image

Programowanie C++

8

Przekazywanie parametrów

W C++, parametry mogą być przekazywane do funkcji na trzy

sposoby: przez wartość, przez referencję i przez stałą referencję.
Wybór sposobu przekazywania parametrów jest decyzją etapu

projektowania. Np. funkcja DistanceTo mogłaby być

zadeklarowana na różne sposoby:

class Point {
...
double DistanceTo(Point other) const;
...
};

class Point {
...
double DistanceTo(Point other) const;
...
};

class Point {
...
double DistanceTo(Point& other) const;
...
};

class Point {
...
double DistanceTo(Point& other) const;
...
};

class Point {
...
double DistanceTo(const Point& other) const;
...
};

class Point {
...
double DistanceTo(const Point& other) const;
...
};

Przez

wartość

Przez referencję

Preferujemy:

Przez stałą referencję

background image

Programowanie C++

9

Przez wartość i stałą referencję

Parametry typów prostych (int, double, char lub wskaźnikowe)

przekazuje się przez wartość

Parametry typów obiektowych przekazujemy przez stałą

referencję

class Point {
...
Point(double x, double y);
...
void Set(double newX, double newY);
void Move(double deltaX, double deltaY);
...
};

class Point {
...
Point(double x, double y);
...
void Set(double newX, double newY);
void Move(double deltaX, double deltaY);
...
};

class Point {
...
double DistanceTo(const Point& other) const;
...
};

class Point {
...
double DistanceTo(const Point& other) const;
...
};

background image

Programowanie C++

10

Definiowanie funkcji

(1)

Funkcje mogą być zdefiniowane w innym

pliku:

double Point::X() const
{
return x;
}

double Point::X() const
{
return x;
}

double Point::Y() const
{
return y;
}

double Point::Y() const
{
return y;
}

void Point::Set(double newX, double newY)
{
x = newX;
y = newY;
}

void Point::Set(double newX, double newY)
{
x = newX;
y = newY;
}

void Point::Move(double deltaX, double
deltaY)
{
x += deltaX;
y += deltaY;
}

void Point::Move(double deltaX, double
deltaY)
{
x += deltaX;
y += deltaY;
}

Selektory

zwracają

wartości danych

prywatnych

Ten modyfikator przypisuje

nowe wartości danym

prywatnym.

Ten modyfikator dodaje

wartości do danych

prywatnych

Definiując funkcje składowe klasy trzeba uwzględnić nazwę klasy

background image

Programowanie C++

11

Przypisanie dla klas

Czy można użyć instrukcji przypisania dla obiektów klasy? Tak!
Domyślnie oznacza to przypisanie danych składowych. Zatem, jeśli

p1 i p2 są obiektami klasy Point, zapis

p1 = p2;

p1 = p2;

daje w efekcie:

p1.x = p2.x;
p1.y = p2.y;

p1.x = p2.x;
p1.y = p2.y;

Zauważ, że x i y są

prywatnymi danymi

składowymi, które nie

mogą być używane poza

klasą.

W przypadku wielu interesujących klas, przypisanie obiektu do

innego obiektu nie jest prostym przypisaniem składnika do

składnika. W takich przypadkach będziemy musieli zdefiniować

znaczenie operatora przypisania dla klasy jako funkcję składową.

(O tym później)

background image

Programowanie C++

12

Definicje funkcji

(2)

Odległość obliczana jest zgodnie z twierdzeniem Pitagorasa:

double Point::DistanceTo(const Point& other) const
{
return sqrt(pow(this->x - other.x, 2) + pow(this->y - other.y, 2));
}

double Point::DistanceTo(const Point& other) const
{
return sqrt(pow(this->x - other.x, 2) + pow(this->y - other.y, 2));
}

double Point::DistanceTo(const Point& other) const

{
return sqrt(pow(

this->x

-

other.x

, 2) + pow(

this->y

-

other.y

,2));

}

double Point::DistanceTo(const Point& other) const

{
return sqrt(pow(

this->x

-

other.x

, 2) + pow(

this->y

-

other.y

,2));

}

To jest dana składowa x obiektu,

od którego odległość jest

obliczana...

… a to jest dana składowa x

parametru other.

Operator strzałki -> jest

używany ze wskaźnikami do

obiektów.

Operator kropki jest używany z

obiektami.

this jest wskaźnikiem w funkcji DistanceTo:

background image

Programowanie C++

13

Odniesienie do obiektu

void Point::Display() const
{
cout << x << " " << y;
}

void Point::Display() const
{
cout << x << " " << y;
}

W wielu sytuacjach obiekt w definicji funkcji jest niejawny:

Jeśli chcemy wyrazić go jawnie używamy wskaźnika this:

Technicznie this jest wskaźnikiem do obiektu. Jego wartością jest adres

miejsca w pamięci, gdzie obiekt jest przechowywany. (Ten adres nie ulega

zmianie w czasie życia obiektu.)
Jeśli chcemy odnieść się do obiektu, a nie jego adresu, używamy

operatora gwiazdki aby dokonać dereferencji wskaźnika: *this.

double Point::DistanceTo(const Point& other) const
{
return sqrt(pow(this->x - other.x, 2) + pow(this->y - other.y, 2));
}

double Point::DistanceTo(const Point& other) const
{
return sqrt(pow(this->x - other.x, 2) + pow(this->y - other.y, 2));
}

x i y tutaj to x i y obiektu dla

którego funkcja Display jest

wywołana.

Użycie this tutaj jest kwestią stylu. Pominięcie wskaźnika

this nie zmieniłoby znaczenia funkcji.Taki zapis powoduje

jedynie wyraźne rozróżnienie pomiędzy x i y obiektu oraz x

i y parametru funkcji.

background image

Programowanie C++

14

Definicje konstruktorów

Konstruktory są specjalnymi funkcjami o specjalnym syntaksie.

Dostarczają kodu do zainicjalizowania obiektów.

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

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

Point::Point(double x, double y):
x(x),
y(y)
{
}

Point::Point(double x, double y):
x(x),
y(y)
{
}

Konstruktor domyślny jest bardzo prosty:

To oznacza, że dane składowe x

i y otrzymają początkowo

wartość zero.

Drugi konstruktor jest również prosty:

Pierwsze x jest daną składową; x w nawiasach jest

parametrem. Tak samo dla y. Zatem składowa x otrzymuje

początkowo wartość parametru x, itd. (Moglibyśmy

oczywiście użyć innych nazw, ale po co?…)

W tych przypadkach bloki funkcji są puste, cała praca związana z

inicjalizacją danych wykonywana jest poprzez listę inicjalizacyjną.

background image

Programowanie C++

15

Definiowanie destruktora

Destruktor klasy Point nic nie robi. Zatem jego blok jest pusty:

Point::~Point()
{
}

Point::~Point()
{
}

Oczywiście destruktory nie zawsze są tak proste.Zazwyczaj nie są

zbyt skomplikowane jednakże wymagają sporo uwagi. Wiele

błędów w programach pochodzi od źle zdefiniowanych

destruktorów.

Point::~Point()
{

cout << "Point <";
Display();
cout << "> about to be destroyed..." << endl;

}

Point::~Point()
{

cout << "Point <";
Display();
cout << "> about to be destroyed..." << endl;

}

Warto czasami aby destruktor wyświetlał komunikat „pożegnalny”:

background image

Programowanie C++

16

Użycie klasy Point

Oto prosty program do testowania klasy Point:

int main()

{

double x;

double y;

Point p;

const Point origin;

cout << "First point (initial coordinates): ";

p.Display();

cout << endl;

cout << "New coordinates (x and y), please: ";

cin >> x >> y;

p.Set(x, y);

cout << "First point (new coordinates): ";

p.Display();

cout << endl;

double d;

d = p.DistanceTo(origin);

cout << "First point distance to origin: " << d << endl;

...

}

int main()

{

double x;

double y;

Point p;

const Point origin;

cout << "First point (initial coordinates): ";

p.Display();

cout << endl;

cout << "New coordinates (x and y), please: ";

cin >> x >> y;

p.Set(x, y);

cout << "First point (new coordinates): ";

p.Display();

cout << endl;

double d;

d = p.DistanceTo(origin);

cout << "First point distance to origin: " << d << endl;

...

}

Deklaracja dwóch liczb

rzeczywistych i dwóch

punktów. Oba punkty są

początkowo <0, 0>.

Zmiana wartości punktu

p i wyświetlenie tych

wartości.

Punkt origin jest

stały: można

używać tylko

selektorów.

Obliczanie odległości p od

początku układu

współrzędnych

Funkcja main.

Podobnie jak wszystkie

inne zmienne zmienne

obiektowe również mogą

być deklarowane w

dowolnym miejscu.

background image

Programowanie C++

17

Użycie klasy Point (cd)

...

cout << "Now moving first point" << endl;

cout << "Displacements (x and y), please: ";

double dx;

double dy;

cin >> dx >> dy;

p.Move(dx, dy);

cout << "First point (after having moved): ";

p.Display();

cout << endl;

cout << "Now giving coordinates for second point" << endl;

cout << "Coordinates (x and y) for second point, please: ";

cin >> x >> y;

Point q(x, y);

cout << "Second point (initial coordinates): ";

q.Display();

cout << endl;

cout << "Distance from second point to first: "

<< q.DistanceTo(p) << endl;

cout << "Distance from first point to second: "

<< p.DistanceTo(q) << " (of course it's the same)" << endl;

}

...

cout << "Now moving first point" << endl;

cout << "Displacements (x and y), please: ";

double dx;

double dy;

cin >> dx >> dy;

p.Move(dx, dy);

cout << "First point (after having moved): ";

p.Display();

cout << endl;

cout << "Now giving coordinates for second point" << endl;

cout << "Coordinates (x and y) for second point, please: ";

cin >> x >> y;

Point q(x, y);

cout << "Second point (initial coordinates): ";

q.Display();

cout << endl;

cout << "Distance from second point to first: "

<< q.DistanceTo(p) << endl;

cout << "Distance from first point to second: "

<< p.DistanceTo(q) << " (of course it's the same)" << endl;

}

Przesunięcie punktu

p

Deklaracja punktu q z

wykorzystaniem drugiego

konstruktora

Wyliczenie oraz wyświetlenie

odległości od q do p i

odwrotnie.

Trzy destruktory klasy Point

tutaj wywoływane niejawnie, dla

punktów q, origin i p.

Zauważ, że nie można “wysłać”

punktu bezpośrednio do

strumienia cout:
cout << q << endl;
BŁĄD!

background image

Programowanie C++

18

Modularyzacja

Nasz program jest zapisany w trzech

plikach:

Point.h

Point.h

Point.cpp

Point.cpp

M_Point.cp

M_Point.cp

Plik nagłówkowy dla klasy Point.

Zawiera deklarację klasy Point.

Plik źródłowy klasy Point. Zawiera

definicje wszystkich funkcji

zadeklarowanych w pliku Point.h.

Główny plik programu. Zawiera

funkcję main.

Pliki nagłówkowe mają rozszerzenie “.h”. Pliki źródłowe w C++ mają

rozszerzenie “.cpp” (lub “.cp”, lub “.cxx”, w zależności od platformy).

Pliki C mają rozszerzenie “.c”.

Nazwy plików są dowolne, jednakże powinno się zachować pewne reguły.

Będziemy zawsze nazywali pliki zgodnie z nazwą klasy, której dotyczą i

tak plik nagłówkowy dla klasy X będzie miał nazwę X.h a plik źródłowy

X.cpp.

background image

Programowanie C++

19

Włączanie plików nagłówkowych

Plik źródłowy musi mieć dołączony plik nagłówkowy zawierający

wymagane deklaracje. Plik Point.cpp wymaga pliku Point.h:

#include "Point.h"
Point::Point():

x(0),

y(0)

{

}
...

#include "Point.h"
Point::Point():

x(0),

y(0)

{

}
...

Oczywiście plik Point.cpp wymaga również bibliotecznych plików

nagłówkowych

#include <math.h>

#include <iostream.h>
#include "Point.h"
Point::Point():

x(0),

y(0)

{

}
...

#include <math.h>

#include <iostream.h>
#include "Point.h"
Point::Point():

x(0),

y(0)

{

}
...

Standardowe pliki nagłówkowe pojawiają

się w nawiasach < i > . Pliki nagłówkowe

użytkownika umieszczone są w apostrofach.

background image

Programowanie C++

20

Unikanie wielokrotnych

dyrektyw include

Jeśli plik A włącza pliki B i C, a plik B dołącza plik C, wówczas plik

A dołącza plik C dwukrotnie. Może to spowodować problemy. Aby

ich uniknąć używamy stałych symbolicznych oraz dyrektywy

#define . Plik Point.h ma wówczas postać:

#ifndef _H_Point
#define _H_Point

class Point {
private:
...
public:
...
};

#endif

#ifndef _H_Point
#define _H_Point

class Point {
private:
...
public:
...
};

#endif

Oznacza to: jeśli stała symboliczna

_H_Point nie jest jeszcze

zdefiniowana, wówczas definiujemy ją

i dołączmy resztę pliku. Jeśli jest już

zdefiniowana pomijamy resztę pliku

(aż do dyrektywy #endif).

Wszystkie nasze deklaracje klas powinny być wewnątrz zakresu

#ifndef-#define-#endif. Nazwy stałych symbolicznych to nazwy

klasy poprzedzone znakami _H_.

background image

Programowanie C++

21

Organizacja programu

W najprostszym przypadku plik

źródłowy M_ będzie zawierał tylko

jedną funkcję main:

Znacznie lepiej jest zdefiniować

większą liczbę funkcji testowych i

w funkcji main wywoływać tylko tą,

która nas w danym momencie

interesuje:

int main()

{

Point p;

...

return 0;

}

int main()

{

Point p;

...

return 0;

}

void TestPoints();

void TestDoubleOutput();

int main()

{

TestDoubleOutput();

return 0;

}

void TestPoints()

{

...

}

void TestDoubleOutput()

{

...

}

void TestPoints();

void TestDoubleOutput();

int main()

{

TestDoubleOutput();

return 0;

}

void TestPoints()

{

...

}

void TestDoubleOutput()

{

...

}

background image

Programowanie C++

22

Najczęstsze błędy

• Wywoływanie funkcji bez obiektu (przykład: Display(p)

zamiast p.Display()).

• Brak nazwy klasy w definicji funkcji.
• Próba dostępu do danych prywatnych spoza klasy.
• Próba zmiany wartości obiektu w selektorze.
• Brak dołączenia koniecznych plików nagłówkowych.
• Użycie nawiasów w deklaracji obiektu w przypadku użycia

konstruktora domyślnego.(przykład Point p(); zamiast Point

p;).

• Brak nawiasów w wywołaniu funkcji bezargumentowej.
• Brak modyfikatorów const.
• Przesyłanie parametrów typu klasy przez wartość.
• Średnik po nagłówku funkcji w jej definicji.
• Brak średnika na końcu deklaracji klasy.

background image

Programowanie C++

23

Jeszcze jedna klasa Rational

(1)

class Rational {
private:
int num;
int den;
public:
Rational();
Rational(int num);
Rational(int num, int den);
Rational(const Rational& other);
~Rational();

int Numerator() const;
int Denominator() const;

void Set(int newNum, int newDen);

...
}

Trzy konstruktory mogą

być zastąpione jednym z

domyślnymi parametrami:

Konstruktor

kopiujący.

Dwa naturalne

selektory

Zwyczajowa funkcja

Set.

Rational(int num1=0, int den=1);

Rational(int num1=0, int den=1);

background image

Programowanie C++

24

Konstruktory i destruktor

Rational::Rational():
num(0), den(1)
{ }
Rational::Rational(int num):
num(num), den(1)
{ }
Rational::Rational(int num, int den):
num(num), den(den)
{ }

Rational::Rational(const Rational& other):
num(other.num),
den(other.den)
{}

Rational::~Rational()
{}

Rational::Rational():
num(0), den(1)
{ }
Rational::Rational(int num):
num(num), den(1)
{ }
Rational::Rational(int num, int den):
num(num), den(den)
{ }

Rational::Rational(const Rational& other):
num(other.num),
den(other.den)
{}

Rational::~Rational()
{}

Każda klasa ma przynajmniej dwa

konstruktory: domyślny i kopiujący
class T{
T();
T(const T&);
};
Jeśli klasa nie zawiera konstruktora

kopiującego to jest on tworzony

automatycznie.
Automatyczne wywołanie

konstruktora kopiującego:
-inicjalizacja obiektu w deklaracji,
-przekazanie obiektu do funkcji

przez wartość,
-zwracanie wartości obiektu z

funkcji.

Klasa Point powinna

mieć konstruktor

kopiujący tej postaci.

Nie ma nic do

zrobienia w

destruktorze.

Rational::Rational(int num, int den):
num(num), den(den)
{ }

Rational::Rational(int num, int den):
num(num), den(den)
{ }

background image

Programowanie C++

25

Funkcje składowe

int Rational::Numerator() const
{ return num; }

int Rational::Denominator() const
{ return den; }

void Rational::Display() const
{ cout<<num<<'/'<<den; }

void Rational::Set(int newNum, int newDen)
{ num=newNum; den=newDen;}

int Rational::Gcd(int j, int k)
{
if (k==0) return j;
return Gcd(k,j%k);
}

void Rational::Reduce()
{
int g=Gcd(num,den);
num/=g;
den/=g;
}

int Rational::Numerator() const
{ return num; }

int Rational::Denominator() const
{ return den; }

void Rational::Display() const
{ cout<<num<<'/'<<den; }

void Rational::Set(int newNum, int newDen)
{ num=newNum; den=newDen;}

int Rational::Gcd(int j, int k)
{
if (k==0) return j;
return Gcd(k,j%k);
}

void Rational::Reduce()
{
int g=Gcd(num,den);
num/=g;
den/=g;
}

class Rational{
private:
...
public:
...
private:
int Gcd(int j, int k);
int Reduce();
};

class Rational{
private:
...
public:
...
private:
int Gcd(int j, int k);
int Reduce();
};

Funkcje składowe klasy zwykle są

deklarowane jako public;
Funkcje używane jedynie wewnątrz

klasy mogą być deklarowane jako

private, są to zwykle tzw. funkcje

pomocnicze.

background image

Programowanie C++

26

class Rational{
private:
...
public:
...
Rational& operator = (const Rational &);//
operator przypisania
Rational& operator *=(const Rational&);
Rational operator++(); //
preinkrementacja
Rational operator++(int); //postinkrementacja
operator float() const; // operator konwersji
int& operator[](int); //operator indeksu
...
};

class Rational{
private:
...
public:
...
Rational& operator = (const Rational &);//
operator przypisania
Rational& operator *=(const Rational&);
Rational operator++(); //
preinkrementacja
Rational operator++(int); //postinkrementacja
operator float() const; // operator konwersji
int& operator[](int); //operator indeksu
...
};

Większość operatorów dostępnych w C++ może być przeładowana.
Wszystkie operatory arytmetyczne zarówno jedno jak i dwuargumentowe

mogą być przeładowane, jednakże z zachowaniem ich argumentowości.
Nie można poprzez przeładowanie zmienić priorytetu lub łączności

operatorów.
Nie można również definiować własnych operatorów.

Klasa Rational

(2)

background image

Programowanie C++

27

Definicje operatorów

Rational& Rational::operator=(const Rational& other)
{
num=other.num;
den=other.den;
return *this;
}

Rational& Rational::operator=(const Rational& other)
{
num=other.num;
den=other.den;
return *this;
}

Operator przypisania zwraca referencję do

obiektu, aby umożliwić przypisanie

łańcuchowe.

Inicjalizacja i przypisanie to dwie różne operacje:
Rational x(22,3) // inicjalizacja

Rational y(x);

// inicjalizacja – konstruktor

kopiujący

Rational z = x; //inicjalizacja !!! – konstruktor
kopiujący

Rational w;

w = x;

//przypisanie

Rational& Rational::operator*=(const Rational& other)
{
num*=other.num;
den*=other.den;
Reduce();
return *this;
}

Rational& Rational::operator*=(const Rational& other)
{
num*=other.num;
den*=other.den;
Reduce();
return *this;
}

Wywołanie funkcji składowej, na rzecz

tego samego obiektu

background image

Programowanie C++

28

Klasa Rational

(3)

class Rational{
friend Rational operator*(const Rational&, const Rational&)
friend int operator==(const Rational&, const Rational&)

private:
...
public:
...
};

class Rational{
friend Rational operator*(const Rational&, const Rational&)
friend int operator==(const Rational&, const Rational&)

private:
...
public:
...
};

Operatory w C++ nie muszą być funkcjami składowymi klasy. Zwykłe

operatory arytmetyczne oraz operatory relacji zwykle są definiowane

jako funkcje zaprzyjaźnione

Rational operator*(const Rational& x, const Rational& y)
{
Rational z(x.num*y.num, x.den*y.den);
z.Reduce();
return z;
}

int operator ==(const Rational& x, const Rational& y)
{
return (x.num*y.den==x.den*y.num);
}

Rational operator*(const Rational& x, const Rational& y)
{
Rational z(x.num*y.num, x.den*y.den);
z.Reduce();
return z;
}

int operator ==(const Rational& x, const Rational& y)
{
return (x.num*y.den==x.den*y.num);
}

Definicja funkcji zaprzyjaźnionej:
-brak słowa friend,
-brak nazwy klasy z operatorem

zakresu,
-brak wskaźnika this

background image

Programowanie C++

29

Definicje operatorów c.d.

Rational Rational::operator++()
{
num+=den;
return *this;
}
Rational Rational::operator++(int)
{
Rational temp=*this;
num+=den;
return temp;
}

Rational::operator float() const
{
return float(num)/den;
}

int& Rational::operator[](int i)
{
if (i==1) return num;
if (i==0) return den;
cerr<<"ERROR:index out of range";
exit(0);
}

Rational Rational::operator++()
{
num+=den;
return *this;
}
Rational Rational::operator++(int)
{
Rational temp=*this;
num+=den;
return temp;
}

Rational::operator float() const
{
return float(num)/den;
}

int& Rational::operator[](int i)
{
if (i==1) return num;
if (i==0) return den;
cerr<<"ERROR:index out of range";
exit(0);
}

3

5

3

3

2

1

3

2

Deklaracje operatorów inkrementacji różnią

się tylko parametrem, który w

rzeczywistości nie jest przekazywany w

wywołaniu funkcji dlatego może być

nienazwany.
Pełni on rolę niemego parametru

rozróżniającego operator pre- i post-

Funkcja operatora konwersji (rzutowania)

nie określa zwracanego typu – jest nim typ

konwersji.
Konwersja typu int na typ wbudowany

dokonuje się poprzez konstruktor:

x=Rational(22)
Operator i konstruktor konwersji są

wywoływane automatycznie jeśli to

konieczne.

background image

Programowanie C++

30

Operatory arytmetyczne jako

funkcje składowe vs funkcje

zaprzyjaźnione

class Rational{
friend Rational operator*(const Rational&, const Rational&)

public:
...
Rational operator/(const Rational&, const Rational&)
};

class Rational{
friend Rational operator*(const Rational&, const Rational&)

public:
...
Rational operator/(const Rational&, const Rational&)
};

Operatory ( ), [ ], -> oraz operatory przypisania muszą być

zadeklarowane jako funkcje składowe klasy.
Pozostałe operatory mogą być funkcjami nie należącymi do klasy.
Jeśli nie-składowa funkcja musi działać na danych prywatnych, powinna

być zadeklarowana jako friend.
Operatory niezależnie od tego czy są funkcjami zaprzyjaźnionymi czy

funkcjami składowymi są wywoływane w taki sam sposób.
Jeśli operator jest funkcją składową klasy najbardziej lewostronny

operand musi być obiektem bądź referencją do obiektu danej klasy.
Jeśli lewostronny operand musi być obiektem innej klasy bądź typem

wbudowanym wówczas funkcja operatorowa musi być funkcją nie

należącą do klasy.
Jeśli operator ma być funkcją przemienną wówczas nie powinien być

funkcją składową klasy.

background image

Programowanie C++

31

Operatory << i >>

class Rational{
friend ostream& operator<<(ostream&, const Rational&)
friend istream& operator>>(istream&, Rational&)
...
};

class Rational{
friend ostream& operator<<(ostream&, const Rational&)
friend istream& operator>>(istream&, Rational&)
...
};

Operatory wstawiania do strumienia wyjściowego i pobierania ze

strumienia wejściowego wymagają lewostronnego operandu

odpowiednio ostream& i istream& dlatego nie mogą być funkcjami

składowymi klasy.

ostream& operator<<(ostream& output, const Rational& y)
{
output<<y.num<<'/'<<y.den;
return output;
}

istream& operator>>(istream& input, Rational& y)
{
input>>y.num;
input.ignore();
input>>y.den;
y.Reduce();
return input;
}

ostream& operator<<(ostream& output, const Rational& y)
{
output<<y.num<<'/'<<y.den;
return output;
}

istream& operator>>(istream& input, Rational& y)
{
input>>y.num;
input.ignore();
input>>y.den;
y.Reduce();
return input;
}

background image

Programowanie C++

32

Testujemy operatory klasy

#include "rational.h"
main()
{
Rational u1, u2(2,2);
Rational u3(u2);
cout<<"u1 = "<<u1<<" u2 = "<<u2<<" u3 =
"<<u3<<endl;
cout<<"Podaj ulamek postaci a/b : ";
cin>>u1;
cout<<u1<<" * "<<u2<<" = "<<u1*u2<<endl;
cout<<"Wartość rzeczywista u1 = "<<float(u1)<<endl;
cout<<"Operator indeksu:"<<endl;
cout<<u1[1]<<" "<<u1[2]<<endl;
cout<<"u2++ "<<u2++<<endl;
cout<<"++u2 "<<++u2;
}

#include "rational.h"
main()
{
Rational u1, u2(2,2);
Rational u3(u2);
cout<<"u1 = "<<u1<<" u2 = "<<u2<<" u3 =
"<<u3<<endl;
cout<<"Podaj ulamek postaci a/b : ";
cin>>u1;
cout<<u1<<" * "<<u2<<" = "<<u1*u2<<endl;
cout<<"Wartość rzeczywista u1 = "<<float(u1)<<endl;
cout<<"Operator indeksu:"<<endl;
cout<<u1[1]<<" "<<u1[2]<<endl;
cout<<"u2++ "<<u2++<<endl;
cout<<"++u2 "<<++u2;
}

u1=0/1 u2=2/2 u3=2/2
Podaj ulamek postaci
a/b : ¾
3/4 * 2/2 = 3/4
Wartość rzeczywista u1 =
0.75
Operator indeksu:
3 4
u2++ 2/2
++u2 6/2

u1=0/1 u2=2/2 u3=2/2
Podaj ulamek postaci
a/b : ¾
3/4 * 2/2 = 3/4
Wartość rzeczywista u1 =
0.75
Operator indeksu:
3 4
u2++ 2/2
++u2 6/2

background image

Programowanie C++

33

Kompozycja

#include "string.h"
#include "date.h"
class Person {
private:
String name,nationality;
int sex; //0 – female, 1 - male
Date birthday;
public:
Person(char* n="",char* nat="Polish",int s=1);
~Person();
String Name() const;
String Nationality() const;
int Sex() const;
Date Birthday() const;
void SetBirthday(int , int, int );
void Print()const;
};

Plik person.h

Dane składowe klasy

są obiektami innych

klas

void Person::SetBirthday(int d, int m, int y)
{
birthday.SetDate(d,m,y);
}

void Person::SetBirthday(int d, int m, int y)
{
birthday.SetDate(d,m,y);
}

Funkcja SetDate z klasy Date
wykorzystana w definicji

funkcji
SetBirthday klasy Person

background image

Programowanie C++

34

Kompozycja (c.d)

#include "person.h"
main()
{
Person JK("Jan Kowalski");
JK.Print();
cout<<endl<<JK.Name()<<" is "<<JK.Nationality()<<endl;
JK.SetBirthday(1,1,2000);
cout<<"and he was born "<<JK.Birthday()<<endl;
}

#include "person.h"
main()
{
Person JK("Jan Kowalski");
JK.Print();
cout<<endl<<JK.Name()<<" is "<<JK.Nationality()<<endl;
JK.SetBirthday(1,1,2000);
cout<<"and he was born "<<JK.Birthday()<<endl;
}

Plik mperson.cpp

ostream& operator <<(ostream& ostr, const Date& d)
{
static char* monthName[13]={

"","January","February","March","April",

"May","June","July","August",

"September","October","November","December"};
ostr<<d.day<<' '<<monthName[d.month]<<' '<<d.year;
return ostr;
}

ostream& operator <<(ostream& ostr, const Date& d)
{
static char* monthName[13]={

"","January","February","March","April",

"May","June","July","August",

"September","October","November","December"};
ostr<<d.day<<' '<<monthName[d.month]<<' '<<d.year;
return ostr;
}

Plik date.cpp

Taki zapis jest możliwy

ponieważ w klasie String i

w klasie Date jest

przeładowany operator <<

My name is Jan Kowalski
Jan Kowalski is Polish
and he was born 1
January 2000

My name is Jan Kowalski
Jan Kowalski is Polish
and he was born 1
January 2000

background image

Programowanie C++

35

Dziedziczenie

#include "person.h"
class Student: public Person {
private:
String id;
Date matriculation;
int credits;
public:
Student(char* n="", char* i="", int s=0);
~Student();
void SetDmat(int,int,int);
};

Plik student.h

Klasa Student jest pochodną od

klasy bazowej Person i

automatycznie dziedziczy wszystkie

składniki klasy bazowej

Nie dziedziczy się: konstruktorów, destruktora oraz operatora

przypisania.
Ponadto klasa pochodna może zawierać deklaracje nowych

składników lub zmieniać znaczenie operacji odziedziczonych.

Nowe operacje są niedostępne dla obiektów klasy bazowej.

background image

Programowanie C++

36

Dziedziczenie (c.d)

#include "student.h"
main()
{
Student x("Ann Jones","23456789");
x.SetBirthday(13,5,1981);
x.SetDmat(30,9,2000);
x.Print();
cout<<"\n\t Born: "<<x.BirthDay();
cout<<"\n\t Matriculated: "<<x.Matriculation();
}

My name is Ann Jones

Born: 13 May 1981

Matriculated: 30

September 2000

My name is Ann Jones

Born: 13 May 1981

Matriculated: 30

September 2000

Funkcje składowe klasy Student

Funkcje składowe klasy bazowej

Person

Plik mstudent.cpp

Student::Student(char* n, char* i, int s):
Person(n,s), id(i), credits(0)
{}
Student::~Student()
{}
void Student::SetDmat(int d, int m, int y)
{
birthday.SetDate(d,m,y);
}

Student::Student(char* n, char* i, int s):
Person(n,s), id(i), credits(0)
{}
Student::~Student()
{}
void Student::SetDmat(int d, int m, int y)
{
birthday.SetDate(d,m,y);
}

Użycie konstruktora klasy bazowej

Person
do danych prywatnych nie ma

dostępu bezpośredniego; tylko

poprzez funkcje publiczne

Automatyczne wywołanie

destruktora
klasy bazowej Person

background image

Programowanie C++

37

Składowe protected

Klasa pochodna nie ma bezpośredniego dostępu do danych

składowych zadeklarowanych jako

private

. Istnieje możliwość

udostępnienia danych składowych klasom pochodnym przy

jednoczesnym ograniczeniu dostępu z zewnątrz, poprzez

deklarację

protected

class Person {
protected:
String name,nationality;
int sex; //0 – female, 1 - male
Date birthday;
public:
...
};

W ten sposób dane będą dostępne dla wszystkich
potencjalnych klas pochodnych, natomiast spoza
klas będą

traktowane jak prywatne.

Klasa pochodna ma również możliwość
decydowania
O sposobie dostępu do składowych (tylko
protected
i public, bo private i tak nie są dla niej
dostępne)

class Student: public Person {
...
};

class Student: protected Person {
...
};

class Student: private Person {
...
};

protected protected
public public

protected protected
public protected

protected private
public private

background image

Programowanie C++

38

Zagnieżdżanie zakresów

#include "person.h"
class Student: public Person {
...
public:
void Print()const;
};

#include "person.h"
class Student: public Person {
...
public:
void Print()const;
};

void Student::Print()const
{
Person::Print();
cout<<" and I am a student ";
};

void Student::Print()const
{
Person::Print();
cout<<" and I am a student ";
};

Jeśli w klasie pochodnej zadeklarowana jest funkcja bądź

składowa o nazwie takiej samej jak w klasie bazowej wówczas

następuje zasłanianie – w taki sam sposób jak w zagnieżdżaniu

zakresów. Dostęp do składnika zasłoniętego jest możliwy poprzez

operator zakresu.

#include "student.h"
main()
{
...
cout<<„Student speaking: "<<endl;
x.Print();
cout<<"\nPerson speaking: "<<endl;
x.Person::Print();
}

Student speaking:
My name is Ann Jones and I am a
student
Person speaking:
My name is Ann Jones

Student speaking:
My name is Ann Jones and I am a
student
Person speaking:
My name is Ann Jones

background image

Programowanie C++

39

Konstruktory i destruktory

W hierarchii dziedziczenia każdy konstruktor zanim się „wykona”

wywołuje konstruktor z klasy bazowej, natomiast destruktor

wywołuje swojego przodka po wykonaniu własnej pracy.

class X {
public:
X(){cout<<"X::X() constructor \n";}
~X(){cout<<"X::X() destructor \n";}
};
class Y:public X {
public:
Y(){cout<<"Y::Y() constructor \n";}
~Y(){cout<<"Y::Y() destructor \n";}
};
class Z:public Y {
public:
Z(int n){cout<<"Z::Z(n) constructor \n";}
~Z(){cout<<"Z::Z() destructor \n";}
};
main()
{
Z z(44)
}

class X {
public:
X(){cout<<"X::X() constructor \n";}
~X(){cout<<"X::X() destructor \n";}
};
class Y:public X {
public:
Y(){cout<<"Y::Y() constructor \n";}
~Y(){cout<<"Y::Y() destructor \n";}
};
class Z:public Y {
public:
Z(int n){cout<<"Z::Z(n) constructor \n";}
~Z(){cout<<"Z::Z() destructor \n";}
};
main()
{
Z z(44)
}

X::X() constructor
Y::Y() constructor
Z::Z(n) constructor
Z::Z() destructor
Y::Y() destructor
X::X() destructor

X::X() constructor
Y::Y() constructor
Z::Z(n) constructor
Z::Z() destructor
Y::Y() destructor
X::X() destructor

background image

Programowanie C++

40

Funkcje wirtualne (1)

main()
{
cout<<"Zmienne: \n";
Student x("Ann Jones");
x.Print();
cout<<endl;
Person y("John Smith");
y.Print();
cout<<„\nWskaźnik: \n";
Person* z=&y;
z->Print();
z=&x;
z->Print();
}

Funkcje wirtualne gwarantują tzw.
dynamiczne wiązanie czyli wywołanie
funkcji jest odłożone do czasu realizacji
programu i jest zależne od obiektu,
na rzecz którego dana funkcja jest
wywoływana.

Zmienne:
My name is Ann Jones and I am a student
My name is John Smith
Wskaźnik:
My name is John Smith
My name is Ann Jones

class Person {
...
virtual void Print ( ) const;
...
};

Wiązanie statyczne, zachodzące na etapie
kompilacji

Zmienne:
My name is Ann Jones and I am a student
My name is John Smith
Wskaźnik:
My name is John Smith
My name is Ann Jones and I am a student

background image

Programowanie C++

41

Funkcje wirtualne(2)

class X{
public:
void f() {cout<<"X::f() executing \n";}
};
class Y: public X{
public:
void f(){cout<<"Y::f() executing \n";}
};
main()
{
X x;
Y y;
X* p=&x;
p->f();
p=&y;
p->f();
}

X::f() executing
X::f() executing

class X{
public:
virtual void f() {cout<<"X::f() executing \n";}
};
class Y: public X{
public:
void f(){cout<<"Y::f() executing \n";}
};
main()
{
X x;
Y y;
X* p=&x;
p->f();
p=&y;
p->f();
}

X::f() executing
Y::f() executing

Polimorfizm jest możliwy ponieważ wskaźnik klasy bazowej może wskazywać
na obiekt dowolnej klasy pochodnej.

background image

Programowanie C++

42

Polimorfizm

Polimorfizm - pozwala by obiekty różnych typów powodowały

różne działanie w wywołaniu tej samej funkcji. Jest to możliwe

dzięki temu, że wskaźnik do obiektu klasy bazowej może

również pokazywać na obiekty klas pochodnych.

• Aby uzyskać wiązanie dynamiczne parametry muszą być przekazywane

przez wskaźnik bądź referencję

• Słowo virtual pojawia się tylko w deklaracji funkcji w klasie

podstawowej, nie musi pojawić w klasie pochodnej

• Jeśli klasa bazowa deklaruje funkcję wirtualną to musi zawierać jej

definicję nawet jeśli ciało funkcji jest puste

• Klasa pochodna nie musi definiować ponownie funkcji wirtualnej,

wówczas domyślnie wywoływana jest funkcja z klasy bazowej

• Klasa pochodna we własnej definicji nie może zmieniać typu

zwracanego przez funkcję wirtualną

background image

Programowanie C++

43

Wirtualne destruktory

Destruktory definiujemy jako funkcje wirtualne!

class X {
private:
int* p;
public:
X() {p = new int[2]; cout<<” X(). ”;}
~X() { delete [] p; cout<<”~X (). \n”;}
};
class Y: public X{
private:
int* q;
public:
Y() {q = new int[100]; cout<<”Y() : Y::q = ”<<q<<”. ”;}
~Y() { delete [] q; cout<<”~Y() ”;}
};

main ( )
{ for (int i = 0; i<3; i++) {
X* r = new Y;
delete r;
}

X (). Y() : Y::q = 0x0d18. ~X().
X (). Y() : Y::q = 0x0de4. ~X().
X (). Y() : Y::q = 0x0eb0. ~X().

virtual ~X() { delete [] p; cout<<”~X (). \n”;}

X (). Y() : Y::q = 0x0d24. ~Y() ~X().
X (). Y() : Y::q = 0x0d24. ~Y() ~X().
X (). Y() : Y::q = 0x0d24. ~Y() ~X().

background image

Programowanie C++

44

Czyste funkcje wirtualne i klasy

abstrakcyjne

Med
ia

Audio

Book

Periodical

CD

Magazine

Newspaper Journal

Tape

Record

Czysta funkcja wirtualna - funkcja nie mająca w swojej

klasie implementacji.

virtual void f( ) = 0;

Abstrakcyjna klasa bazowa - klasa posiadająca jedną lub więcej

czystych funkcji wirtualnych.

class Media{
protected:
String title;
public:
virtual void print( ) = 0;
virtual char* id( ) = 0;
};
class Book: public Media{
private:
String author, publisher,isbn;
public:
void print ( )
{ cout<<title<<" by "<<author;}
void id ( ) { cout<<isbn; }
};

background image

Programowanie C++

45

Dziedziczenie - abstrakcyjna

klasa bazowa

//Definition of abstract base class SHAPE
// plik shape.h

#ifndef _H_Shape
#define _H_Shape
#include <iostream.h>
class Shape{
public:
virtual double Area() const {return 0.0;}
virtual double Volume() const {return 0.0;}

// pure virtual function overridden in derived classes
virtual void PrintShapeName() const = 0;
virtual void Print() const = 0;
};
#endif

Funkcje wirtualne wraz z domyślną implementacją;
Klasy pochodne nie definiujące własnych wersji
tych funkcji odziedziczą implementację.

Czyste funkcje wirtualne;
Jeśli w klasie pochodnejnie znajdą się implementacje
tych funkcji, to klasy pochodne będą również klasami
abstrakcyjnymi.

background image

Programowanie C++

46

Pochodna klasy bazowej Shape

// pochodna klasy bazowej Shape
//plik point1.h

#ifndef _H_Point1
#define _H_Point1
#include "shape.h"

class Point:public Shape{
private:
int x,y;
public:
Point( int =0,int=0); //default constructor
void SetPoint(int a,int b);
int X() const {return x;}
int Y() const {return y;}
virtual void PrintShapeName() const;
virtual void Print() const;
};
#endif

Dziedziczenie publiczne

Dane prywatne
i dodatkowe funkcje
“wzbogacające” klasę Point w stosunku do klasy
Shape

//plik point1.cpp
#include <iostream.h>
#include "point1.h"
Point::Point(int x,int y):
x(x),y(y)
{}
void Point::SetPoint(int a, int b)
{
x=a;
y=b;
}
void Point::PrintShapeName() const
{
cout<<"Point: ";
}
void Point::Print() const
{
cout<<"[ "<<x<<','<<y<<" ]";
}

background image

Programowanie C++

47

Klasa pochodna od klasy Point

//plik circle.h

#ifndef _H_Circle
#define _H_Circle
#include "point1.h"
class Circle: public Point{
private:
double radius;
public:
Circle(double r =0.0, int x=0, int y=0);
void SetRadius(double );
double Radius() const;
virtual double Area() const;
virtual void PrintShapeName() const;
virtual void Print() const;
};
#endif

Jawne odwołanie do funkcji
z klasy bazowej

//plik circle.cpp

#include "circle.h"
Circle::Circle(double r, int a, int b):
Point(a,b), radius(r)
{}
void Circle::SetRadius(double r)
{ radius = r > 0 ? r : 0; }
double Circle::Radius() const
{ return radius;}
double Circle::Area() const
{
return 3.14159*radius*radius;
}
void Circle::PrintShapeName() const
{
cout<<"Circle : ";
}
void Circle::Print() const
{
Point::Print();
cout<<"; Radius = "<<radius;
}

background image

Programowanie C++

48

Jeszcze jeden poziom

dziedziczenia

// plik cylinder.h

#ifndef _H_Cylinder
#define _H_Cylinder
#include "circle.h"
class Cylinder:public Circle{
private:
double height;
public:
Cylinder(double h=0.0, double r=0.0, int x=0, int y=0);
void SetHeight(double);
double Height() const;
virtual double Area() const;
virtual double Volume() const;
virtual void PrintShapeName() const;
virtual void Print() const;
};
#endif

//plik cylinder.cpp

#include "cylinder.h"
Cylinder::Cylinder(double h, double r, int x,
int y):
Circle(r,x,y), height(h)
{}
void Cylinder::SetHeight(double h)
{ height=h>0 ? h : 0; }
double Cylinder::Height() const
{ return height; }
double Cylinder::Area() const
{ return 2*Circle::Area()
+2*3.14159*Radius()*height; }
double Cylinder::Volume() const
{ return Circle::Area()*height;}
void Cylinder::PrintShapeName() const
{
cout<<"Cylinder : ";
}
void Cylinder::Print() const
{
Circle::Print();
cout<<"; Height = "<<height;
}

background image

Programowanie C++

49

Jak to wszystko działa?

// plik mshape.cpp

#include <iostream.h>
#include <iomanip.h>
#include <conio.h>
#include "shape.h"
#include "point1.h"
#include "circle.h"
#include "cylinder.h"

void VirtualViaPointer(const Shape*);
void VirtualViaReference(const Shape&);
main()
{
clrscr();
cout<<setiosflags(ios::fixed|
ios::showpoint)<<setprecision(2);
Point p(7,11);
p.PrintShapeName();
p.Print();
cout<<endl;
...

...
Circle c(3.5,22,8);
c.PrintShapeName();
c.Print();
cout<<endl;

Cylinder cyl(10,3.3,10,10);
cyl.PrintShapeName();
cyl.Print();
cout<<endl<<endl;
...

Point: [ 7,11 ]
Circle : [ 22,8 ]; Radius = 3.50
Cylinder : [ 10,10 ]; Radius = 3.30; Height = 10.00

background image

Programowanie C++

50

Funkcje wirtualne w akcji

...
Shape* arrayOfShapes[3];

arrayOfShapes[0]=&p;
arrayOfShapes[1]=&c;
arrayOfShapes[2]=&cyl;
cout<<" Virtual calls via base-class pointers\n";
for(int i=0; i<3; i++)
VirtualViaPointer(arrayOfShapes[i]);
cout<<endl;
...

void VirtualViaPointer(const Shape* baseClassPtr)
{
baseClassPtr->PrintShapeName();
baseClassPtr->Print();
cout<<"\nArea = "<<baseClassPtr->Area()
<<"\nVolume = "<<baseClassPtr->Volume()<<endl;
}

Virtual calls via base-class pointers
Point: [ 7,11 ]
Area = 0.00
Volume = 0.0
Circle : [ 22,8 ]; Radius = 3.50
Area = 38.48
Volume = 0.0
Cylinder : [ 10,10 ]; Radius = 3.30; Height = 10.00
Area = 275.77
Volume = 342.12

background image

Programowanie C++

51

Funkcje wirtualne w akcji c.d.

...
cout<<" Virtual calls via base-class references\n";
for( i=0; i<3; i++)
VirtualViaReference(*arrayOfShapes[i]);
cout<<endl;
return 0;
}

void VirtualViaReference(const Shape& baseClassRef)
{
baseClassRef.PrintShapeName();
baseClassRef.Print();
cout<<"\nArea = "<<baseClassRef.Area()
<<"\nVolume = "<<baseClassRef.Volume()<<endl;
}

Virtual calls via base-class pointers
Point: [ 7,11 ]
Area = 0.00
Volume = 0.0
Circle : [ 22,8 ]; Radius = 3.50
Area = 38.48
Volume = 0.0
Cylinder : [ 10,10 ]; Radius = 3.30; Height = 10.00
Area = 275.77
Volume = 342.12


Document Outline


Wyszukiwarka

Podobne podstrony:
cpp z ccfd, pocpp lab7
dane w pigulce 2 cpp
Lab3 Cpp GPS opis
borland cpp builder cw10
borland cpp builder cw13
Lab cpp 12
borland cpp builder cw9
CPP i CPA dla IIA
Programowanie obiektowe, CPP program, 1
Cpp 2, Sortowanie
wyklad5.cpp, JAVA jest językiem programowania obiektowego
borland cpp builder cw2
48 lekcji cpp 5 id 609518 Nieznany (2)
cw4 cpp
egz cpp termin 2abc, Mechatronika, 1 Rok
wyklad4 cpp, Baza danych studentów
wyklad4 cpp, Baza danych studentów
cw1 cpp
cw4 cpp

więcej podobnych podstron