background image

Podstawy

programowania
obiektowego

Ćwiczenia laboratoryjne nr 3

zajęcia zaplanowane na 4 godziny

Temat: Operatory przeciążone, funkcje operatorowe

Prowadzący:

mgr inż. Dariusz Rataj

Koszalin 2001

background image

Podstawy programowania obiektowego ćw nr 2

Strona 2

Spis treści:

1. Przeciążanie operatorów

2. Funkcje operatorowe składowe klasy
3. Funkcje operatorowe zaprzyjaźnione

4. Przykłady

1.  Przeciążanie operatorów

Przeciążanie operatora oznacza zdefiniowanie nowego działania operatora dla definiowanej
klasy. W języku C++ mamy możliwość przedefiniowania działania (przeciążenia) prawie
wszystkich operatorów.  Wyjątkami są operatory:

. .* ?: :: sizeof

Tworząc nową definicję działania operatora nie zmieniamy jego działania dla typów
standardowych, np.: definiując operator + dla nowotworzonej klasy, działanie tego
operatora dla liczb typu int lub float pozostanie niezmienione. Aby zdefiniować działanie
operatora należy utworzyć funkcję operatorową.
W naszym ćwiczeniu zajmiemy się definicjami podstawowych operatorów dwu
i jednoargumentowych. Funkcje operatorowe możemy zdefiniować jako funkcje składowe
klasy lub jako funkcje zaprzyjaźnione klasy (znane z ćwiczenia nr 2).

2.  Funkcje operatorowe składowe klasy

-  operatory dwuargumentowe

Operator dwuargumentowy, zdefiniowany jako funkcja składowa klasy, po lewej stronie
zawsze ma argument  typu definiowanej klasy. W naszym przykładzie będzie to typ
zespolona.
Przykład:

!

  Deklaracja w nagłówku klasy

!

  Definicja metody

  zespolona zespolona::operator + (zespolona z)
  {
   zespolona z1(re+z.re, im+z.im);
   return z1;
  }

zasady stosowania:

Typ zwracany
przez operator

Słowo kluczowe
"operator"

symbol
operatora

Prawy argument
operatora
(typ identyfikator)

zespolona operator  + (zespolona z);

background image

Podstawy programowania obiektowego

Strona 3

  stosujemy wtedy, gdy lewy argument jest tego samego typu co klasa, np. w operatorach

+ , -, *, /, =, = =,

  definicja metody operatorowej poza klasą posiada identyfikator klasy: zespolona:: , tak

samo jak każda inna metoda klasy.

-  operatory jednoargumentowe

!

  Deklaracja w nagłówku klasy

      zespolona operator * ();

!

  Definicja metody

      zespolona zespolona::operator * ()
      {

    return zespolona(re, -im);

      }

3.  Funkcje operatorowe zaprzyjaźnione

Tak zdefiniowany operator po prawej i lewej stronie ma argumenty  dowolnego typu.

Przykład:

!

  Deklaracja w nagłówku klasy

!

  Definicja metody

ostream& operator << (ostream& o, zespolona z)
{

return o << '(' << z.re << ")+(j" << z.im << ')';

}

zasady stosowania:

  stosujemy wtedy, gdy lewy argument jest innego typu (może być ten sam typ) co klasa,

np. w operatorach << ,>>. Przeważnie prawy argument jest tego samego typu co
definiowana klasa,

  deklaracja metody operatorowej w nagłówku klasy posiada słowo kluczowe friend

informujące, że jest to metoda zaprzyjażniona (nie jest metodą składową klasy!). W
definicji metody to słowo nie występuje.

Typ zwracany
przez operator

Słowo kluczowe
"operator"

symbol
operatora

lewy argument
operatora
(typ identyfikator)

prawy argument
operatora
(typ identyfikator)

Słowo kluczowe
"friend" - metoda
zaprzyjaźniona

friend ostream& operator << (ostream& o, zespolona

 z);

background image

Podstawy programowania obiektowego ćw nr 2

Strona 4

  przeważnie typ zwracany przez metodę operatorową jest taki sam jak typ lewego

argumentu (możemy przyjąć to jako zasadę). W naszym przykładzie typ ostream&
(referencja na ostream).

4.  Przykłady

Przykład  1. Definicja klasy osoba. Klasa zawiera trzy pola prywatne: nazwisko, imie, pesel typu

tekstowego (tablica znaków), dwie funkcje operatorowe zaprzyjaźnione: operator wyjścia << i

operator wejścia >>.

#include <iostream.h> 

// cin, cout, ostream, istream

class

 osoba

{
 

private

:

  

char

 nazwisko[

30

], imie[

20

], pesel[

12

];

 

public

:

  osoba();
  

// operator wyjscia drukuje dane na konsoli

  friend ostream& operator <<(ostream& out, osoba& o);
  

// operator wejscia pobiera dane z konsoli

  friend istream& operator >>(istream& in, osoba& o);
};

osoba::osoba()
{
 nazwisko[

0

] = 

0

// pierwszy znak tablicy = 0 -> tekst pusty

 imie[

0

] = 

0

;

 pesel[

0

] = 

0

;

}

ostream& operator << (ostream &out, osoba& o)  

// op.wyjscia

{
 out << o.nazwisko << 

" "

;

 out << o.imie << 

" "

;

 out << o.pesel << 

" "

;

 

return

 out;

}

istream& operator >> (istream &in, osoba& o)  

// op.wyjscia

{
 in >> o.nazwisko >> o.imie >> o.pesel;
 

return

 in;

}

void

 main()

{
 osoba o;      

// deklaracja obiektu osoba

 cout << 

"\n podaj nazwisko, imie i pesel\n"

;

 cin >> o;     

// wprowadzenie danych do obiektu

 cout << o;    

// wyprowadzenie danych na ekran

}

background image

Podstawy programowania obiektowego

Strona 5

Przykład 2. Definicja klasy Plik umożliwiającą wyprowadzenie zawartości pliku na ekran. Klasa zawiera

jedno pole prywatne plik typu FILE * (struktura opisująca strumień - plik dyskowy), funkcję

operatorową zaprzyjaźnioną definiującą operator wyjścia << .

#include <stdio.h>      

// FILE, fopen, fclose, ...

#include <iostream.h>   

// cout, cin, ostream

class

 Plik

{
 

private

:

  FILE* plik;
 

public

:

  Plik(

char

 *NazwaPliku); 

// konstruktor otwiera plik dyskowy

  ~Plik();                

// destruktor zamyka plik dyskowy

  

// operator wyjscia drukuje plik na konsoli

  friend ostream& operator <<(ostream& out, Plik & pl);
};

Plik::Plik(

char

 *NazwaPliku)

{
  

if

 ((plik = fopen(NazwaPliku, 

"rt"

)) == 

NULL

)

  {
   fprintf(stderr, 

"Nie moge otworzyc pliku!!!.\n"

);

  }
}

Plik::~Plik()
{
 

if

 (plik) fclose(plik);

}

ostream & operator << (ostream &out, Plik &pl)  

// op.wyjscia

{
 

char

 ch;

 fseek(pl.plik, 

0

, SEEK_SET); 

// na poczatek pliku

 

do

  {
   ch = fgetc(pl.plik);
   out << ch;
  }
   

while

 (ch != EOF);

 

return

 out;

}

void

 main()

{
 Plik p(

"autoexec.bat"

);

 cout << p;
}

background image

Podstawy programowania obiektowego ćw nr 2

Strona 6

Przykład 3. Definicja klasy zespolona. Przykład ten jest rozszerzeniem przykładu z ćwiczenia nr 2

o szereg operatorów jedno i dwuargumentowych. Funkcje operatorowe zostały zdefiniowane jako

składowe klasy lub jako funkcje zaprzyjaźnione. Przykład do samodzielnej analizy.

#include <iostream.h>

// cin, cout, istream, ostream

#include <math.h>

// fabs, sqrt

#include <conio.h>

// clrscr, getch

typedef enum BOOL { FALSE = 

0

, TRUE };

// deklaracja klasy (interfejs klasy)

class

 zespolona

{
 

private

:

double

 re, im;

 

public

:

zespolona() { re = 

0

; im = 

0

; }

zespolona(

double

 r, 

double

 i = 

0

): re(r), im(i) { }

void

 ustaw(

double

 r, 

double

 i) { re = r; im = i; }

// przeciazone operatory

zespolona operator * ();
zespolona operator + (zespolona z);
friend zespolona operator - (zespolona z1, zespolona z2);
zespolona& operator += (zespolona z);
friend ostream& operator << (ostream &os, zespolona z);
friend istream& operator >> (istream &is, zespolona &z);
friend BOOL operator == (zespolona z1, zespolona z2);

};

// definicja klasy (implementacja klasy), tzn. definicje funkcji
// skladowych klasy i funkcji zaprzyjaznionych z klasa

zespolona zespolona::operator * ()
{
 

return

 zespolona(re, -im);

}

zespolona zespolona::operator + (zespolona z)
{
 

return

 zespolona(re+z.re, im+z.im);

}

zespolona operator - (zespolona z1, zespolona z2)
{
 

return

 zespolona(z1.re-z2.re, z1.im-z2.im);

}

zespolona& zespolona::operator += (zespolona z)
{
 re += z.re; im += z.im;
 

return

 *this;

}

ostream& operator << (ostream &os, zespolona z)
{
 

return

 os << 

'('

 << z.re << 

", "

 << z.im << 

')'

;

}

istream& operator >> (istream &is, zespolona &z)
{
 cout << 

"re = "

; is >> z.re;

 cout << 

"im = "

; is >> z.im;

 

return

 is;

}

BOOL operator == (zespolona z1, zespolona z2)

background image

Podstawy programowania obiektowego

Strona 7

{
 

if

 ( fabs(z1.re-z2.re) < 

1e-10

 && fabs(z1.im-z2.im) < 

1e-10

 )

return

 TRUE;

 

else

return

 FALSE;

}

int

 main()

{

zespolona z1, z2, z3(

1

), z4(

2

3

);

zespolona z5 = z4; 

// inicjalizacja

clrscr();
cout << 

"z1 = "

 << z1 << 

"\tz1"

 << endl; 

// operator << (cout, z1);

cout << 

"z2 = "

 << z2 << 

"\tz2"

 << endl;

cout << 

"z3 = "

 << z3 << 

"\tz3(1)"

 << endl;

cout << 

"z4 = "

 << z4 << 

"\tz4(2, 3)"

 << endl;

cout << 

"z5 = "

 << z5 << 

"\tz5 = z4"

 << endl << endl;

cout << 

"Podaj z1:"

 << endl;

cin >> z1;

// operator >> (cin, z1);

z2.ustaw(

3

, -

4

);

z3 = *z1;

// z3 = z1.operator * ();

z4 = z1 + z2;

// z4 = z1.operator + (z2);

z5 = z1 - z2;

// z5 = operator - (z1, z2);

cout << 

"z1 = "

 << z1 << 

"\tz klawiatury"

 << endl;

cout << 

"z2 = "

 << z2 << 

"\tustaw(3, -4)"

 << endl;

cout << 

"z3 = "

 << z3 << 

"\tsprzezona do z1"

 << endl;

cout << 

"z4 = "

 << z4 << 

"\t= z1 + z2"

 << endl;

cout << 

"z5 = "

 << z5 << 

"\t= z1 - z2"

 << endl << endl;

z4 = z1 + 

2

;

// nie mozna: z4 = 2 + z1;

cout << 

"z4 = "

 << z4 << 

"\t= z1 + 2"

 << endl;

z4 = z1 - 

2

;

//     mozna: z4 = 2 - z1;

cout << 

"z4 = "

 << z4 << 

"\t= z1 - 2"

 << endl;

z5 = z1 + z2 + 

2

 - 

1

 - *z1 + z1 + z2 - 

1.5

;

cout << 

"z5 = "

 << z5;

cout << 

"\t= z1 + z2 + 2 - 1 - *z1 + z1 + z2 - 1.5"

 << endl;

z1 = z2;

// podstawienie

cout << 

"z1 = "

 << z1 << 

"\tz1 = z2"

 << endl;

z1 += z2;

// z1.operator += (z2);

cout << 

"z1 = "

 << z1 << 

"\tz1 += z2"

 << endl;

if

 (z1 == z2)

// if ( operator == (z1, z2) )

cout << 

"z1 jest rowne z2"

 << endl;

else

cout << 

"z1 jest rozne od z2"

 << endl;

cout << endl << 

"Nacisnij dowolny klawisz..."

;

getch();

return

 

0

;

}

background image

Podstawy programowania obiektowego ćw nr 2

Strona 8

Zadania do wykonania na zajęciach i w domu:

1.  Utwórz klasę wektor - jednowymiarową tablicę wartości typu float. Klasa powinna zawierać:

  pole prywatne p - wskaźnik na początek tablicy wartości typu float,

  konstruktor z parametrem typu int - rozmiarem tablicy tworzący dynamicznie tablicę (new) o
odpowiednim rozmiarze,

  destruktor zwalniający pamięć zarezerwowaną przez konstruktor,

  funkcję operatorową >>, tak aby można było wprowadzać dane z klawiatury do wektora,

  funkcję operatorową <<, tak aby można było wyprowadzać dane z wektora na ekran.

2.  Utwórz klasę wektor - jednowymiarową tablicę wartości typu int. Klasa powinna zawierać:

  pole prywatne tab - wskaźnik na początek tablicy wartości typy int,

  konstruktor z parametrem typu int - rozmiarem tablicy tworzący dynamicznie tablicę (new) o

odpowiednim rozmiarze,

  destruktor zwalniający pamięć zarezerwowaną przez konstruktor,

  funkcję operatorową >>, tak aby można było wprowadzać dane z klawiatury do wektora,

  funkcję operatorową <<, tak aby można było wyprowadzać dane z wektora na ekran.

3.  Dla przykładu nr 2 (klasa Plik) rozszerzyć możliwości klasy o funkcje:

  konstruktor przeciążony tworzący nowy plik dyskowy,

  funkcje operatorową >>, tak aby można było wprowadzać dane z klawiatury do nowego pliku

dyskowego,

  funkcję operatorową =, tak aby można było przypisać zawartość pliku,

  funkcję operatorową +, tak aby można było dodawać zawartości dwóch plików. Wraz z

operatorem = otrzymamy możliwość wykonania działania:

void main()
{
  Plik plik1("pl1.txt"), plik2("pl2.txt"), plik1("pl2.txt");
  plik1 = plik2 + plik3;
}

4.  Dla przykładu nr 2 (klasa Plik) rozszerzyć możliwości klasy o funkcje:

  konstruktor przeciążony tworzący nowy plik dyskowy,

  funkcje operatorowe <<, tak aby można było wykonać działanie:

void main()
{
  float f = 2.34;
  int i = 5;
  char tekst[] = "Hallo - to tekst";

  Plik plik1("pl1.txt");
  plik1 << f;

      // zapis do pliku wartości typu 

float

  plik1 << i;      // za

pis do pliku wartości typu 

int

  plik1 << tekst;  // zapis do pliku tekstu z tablicy
}

 Uwaga!!! Konieczne jest zdefiniowanie trzech oddzielnych operatorów << dla każdego typu danych.

5.  Utwórz klasę okrag - obiekt graficzny. Klasa powinna zawierać:

  pola prywatne r - promień, x - wsp. x środka, y - wsp. y środka,

  konstruktor z parametrami: promien, wsp. x i y środka,

  metodę rysuj rysującą okrag w trybie graficznym systemu DOS,

  funkcje operatorową ++ przesuwającą okrąg o 1 punkt w prawo,

  funkcje operatorową -- przesuwającą okrąg o 1 punkt w lewo.

  funkcje operatorową + dodającą dwa okręgi (działanie dodawania można przyjąć dowolne),

  funkcje operatorową = umożliwiającą przypisanie,

  funkcje operatorową == porównującą dwa okręgi.