• Nie Znaleziono Wyników

Wykład 4

N/A
N/A
Protected

Academic year: 2021

Share "Wykład 4"

Copied!
1
0
0

Pełen tekst

(1)

Wykład 4

1.

Obiekty dynamiczne - przeciążanie operatorów new i delete

2. Obiekty z dynamicznymi polami typu jednowymiarowa tablica elementów typu struktura. Przeciążanie operatorów: << , >>, []

3.

Obiekty z dynamicznymi polami typu dwuwymiarowa tablica elementów typu int- przeciążanie operatora << na rzecz dowolnej klasy oraz operatora wywołania funkcji ()

4.

Konwersje typów definiowane przez użytkownika - przeciążanie operatora rzutowania operator typ ()

5. Konwersje typów definiowane przez użytkownika - konwersje z użyciem jednoargumentowych konstruktorów.

1. Obiekty dynamiczne - przeciążanie operatorów new i delete

Operatory new i delete mogą mieć znaczenie globalne, natomiast przeciążone na

(2)

rzecz klasy muszą być metodami typu static (deklarowane również niejawnie jako static) o następujących prototypach:

void * new (size_t)

//zwraca adres przydzielonego obiektu

void delete (typ *)

//funkcja zwalnia obiekt o adresie typ*

Zastosowanie przeciążonych operatorów new i delete umożliwia kontrolę nad tworzonymi obiektami dynamicznymi.

Przykład 1. W programie zlicza się obiekty dynamiczne typu punkt za pomocą pól statycznych aktualizowanych w przeciążonych operatorach new i delete oraz zlicza się wszystkie obiekty w konstruktorach i destruktorze

...

#include "punkt4_1.h"

char s1[] = "Wszystkie punkty = "; //zainicjowane tablice znaków w postaci

char s2[] = "Dynamiczne punkty = "; //łańcuchów znaków zakończonych znakiem \0.

void wyswietl(punkt&);

void wyswietl(punkt*);

void wyswietl(int, char[]);

void main() { punkt *p1;

{ wyswietl(punkt::liczba_punktow(),s1); //0 obiektów wszystkich

wyswietl(punkt::liczba_p_dyn(),s2); //0 obiektów dynamicznych

p1= new punkt(2, 2); //wywołanie 2x przeciążonego operatora new punkt *p2= new punkt(1, 5); //konstruktor zwykły, obiekt dynamiczny

wyswietl(punkt::liczba_punktow(), s1); //2 obiekty wszystkie p1 i p2

wyswietl(punkt::liczba_p_dyn(), s2); //2 obiekty dynamiczne p1, p2

wyswietl(*p1); wyswietl(p2);

punkt p3 = *p1;

wyswietl(punkt::liczba_punktow(), s1); //3 obiekty wszystkie: p1, p2, p3

wyswietl(punkt::liczba_p_dyn(), s2); //2 obiekty dynamiczne p1, p2

punkt p4;

wyswietl(punkt::liczba_punktow(),s1); //4 obiekty wszystkie p1,p2,p3,p4

wyswietl(punkt::liczba_p_dyn(),s2); //2 obiekty dynamiczne p1, p2

delete p2; //wywołanie przeciążonego operatora

delete

} /*tutaj koniec bloku i zostają usunięte obiekty p3, p4 oraz wskaźnik p2*/

wyswietl(punkt::liczba_punktow(),s1); //1 obiekt p1

wyswietl(punkt::liczba_p_dyn(),s2); //1 obiekt dynamiczny p1

delete p1; //wywołanie przeciążonego operatora

delete

wyswietl(punkt::liczba_punktow(),s1); //0 obiektów wszystkich

wyswietl(punkt::liczba_p_dyn(),s2); } //0 obiektów dynamicznych

(3)

//plik nagłówkowy punkt4_1.h

#include <stddef.h> //deklaracja size_t class punkt

{ static int ile_punktow;

static int ile_p_dyn;

float x,y;

public: punkt (float=0.0,float=0.0);

punkt(punkt&);

~punkt();

int odcieta();

int rzedna();

static int liczba_punktow();

static int liczba_p_dyn();

void* operator new (size_t); //zawsze typu static void operator delete (void *); //zawsze typu static };

//plik punkt4_1.cpp z definicjami metod ...

#include "punkt4_1.h"

punkt::punkt(float xx, float yy) {... ile_punktow++; } punkt::punkt(punkt& p) {... ile_punktow++; } punkt::~punkt() {... ile_punktow--; } void* punkt::operator new (size_t rozmiar)

{

ile_p_dyn++;

return :: new char [rozmiar];

// :: operator globalności, który pozwala wywołać standardową funkcję new }

void punkt::operator delete (void *wsk) {

ile_p_dyn--;

::delete (wsk);

// :: operator globalności, który pozwala wywołać standardową funkcję delete }

int punkt::ile_punktow = 0;

int punkt::ile_p_dyn = 0;

int punkt::liczba_punktow() { return ile_punktow; } int punkt::liczba_p_dyn() { return ile_p_dyn; }

Inna deklaracja przeciążonego operatora new, umożliwia przekazanie

dodatkowych parametrów, oprócz obowiązkowego size_t:

(4)

void *operator new (size_t, typ_1, ...typ_n);

i jest wywołana:

typ *wsk= new (arg_1,...arg_n) typ;

gdzie typ jest klasą tworzonego obiektu Przykład 2

W programie dodatkowy parametr w przeciążonym operatorze new (oprócz wymaganego typu size_t) pozwala podjąć decyzję o wyświetlaniu komunikatów przez new, gdy parametr jest równy 1.

...

#include "punkt4_2.h"

...

main()

{ punkt *p1;

...

p1 = new (1) punkt(2, 2); //dodatkowy parametr w przeciążonym new punkt *p2 = new(1) punkt(1, 5); //dodatkowy parametr w przeciążonym new ...

}

...

//plik nagłówkowy punkt4_2.h

#include <stddef.h> // dla size_t class punkt

{ static int ile_punktow;

static int ile_p_dyn;

float x,y;

public:

...

void* operator new (size_t, int); //dodatkowy parametr int w przeciaz. new void operator delete (void *);

};

#include "punkt4_2.h" //plik punkt4_2.cpp ...

void* punkt::operator new (size_t rozmiar,int co) { ile_p_dyn++;

if (co) cout << "Przydzial pamieci\n";

return::new char [rozmiar]; } ...

(5)

2. Obiekty z dynamicznymi polami typu jednowymiarowa tablica elementów typu struktura. Przeciążanie operatorów: << , >>, []

Przykład 3

Program w przykładzie 3 wprowadza i wyświetla współrzędne wierzchołków wielokąta oraz rysuje wierzchołki w postaci znaków ¦ w miejscach o podanych współrzędnych. Wielokąt jest zawarty w klasie figura, o maksymalnej liczbie wierzchołków określonej liczbą max.

Obiekt typu figura powinien zawierać:

·

konstruktor zwykły tworzący dynamiczną tablice struktur Figura zawierających współrzędne wierzchołków (x, y); konstruktor kopiujący tworzący dynamiczną tablicę struktur Figura i kopiujący zawartość przekazywanego obiektu oraz destruktor usuwający tę tablicę

·

metodę rysuj, wyświetlającą znak ¦ na ekranie w miejscu (x, y), wtedy gdy znajduje się w obrębie ekranu (1<= x <=80, 1 <=y<=25). Dane każdego wierzchołka pobiera z dynamicznej tablicy struktur Figura.

·

metodę przesun, zmieniającą współrzędne x o dx i y o dy wybranego elementu - wierzchołka z tablicy Figura

·

dwa przeciążone operatory indeksowania: jeden do odczytu i drugi do zapisu wartości x i y wybranego i-tego elementu - wierzchołka z tablicy Figura

·

operator przeciążony >> jako funkcję zaprzyjaźnioną, której lewym argumentem jest obiekt klasy istream, a prawym jest referencja do struktury typu wsp, zdefiniowanego jako prywatny typ klasy figura. Wykorzystanie tej definicji poza blokiem deklaracji klasy jest możliwe dzięki następującej deklaracji

figura::wsp.

W przypadku, gdy struktura typu wsp jest deklaracją public, można stosować nazwę wsp bezpośrednio. Operator przeciążony >> jest wykorzystany do zapisu danych do tablicy struktur Figura, umieszczonej w klasie figura.

·

operator przeciążony << jako funkcja zaprzyjaźniona, której lewym argumentem jest obiekt klasy ostream, a prawym jest struktura typu wsp, zdefiniowanego jako prywatny typ klasy figura.

Operator przeciążony << jest wykorzystany do odczytu danych z tablicy struktur Figura, umieszczonej w klasie figura.

·

metoda koniec do sprawdzenia, czy jest wolne miejsce w tablicy

Należy zabezpieczyć metody przed dostępem do elementów tablicy Figura

poza jej ograniczającymi indeksami 0-max-1 za pomocą metody koniec.

(6)

...

#include "fig4_3.h"

void wprowadz_punkty(figura&);

void wyswietl(const figura);

void main() { do

{ figura wielokat(5);

wprowadz_punkty(wielokat); //sposób wprowadzania obojętny dla klasy

wielokat.rysuj(); //operacja powierzona klasie

wyswietl(wielokat); // operacja poza obowiązkami klasy } while (getch() != 27);}

void wprowadz_punkty(figura& wielokat) { int ile = wielokat.podaj_wsp();

for (int i = 0; i < ile; i++)

{ cout<< "Podaj wspolrzedne x, y dla wielokat["<<i<<"]: ";

cin >> wielokat[i];

clrscr();}}

void wyswietl(const figura wielokat) {int ile = wielokat.podaj_wsp();

for (int i = 0; i < ile ; i++)

cout<< setw(15)<<"Wielokat["<<i<<"] "<<

setw(5)<<wielokat[i]<<endl;}

class figura //plik nagłówkowy fig4_3.h

{ struct wsp {int x, y;};

int max; // maksymalna liczba współrzędnych w tablicy dynamicznej Figura

wsp* Figura; //wskaźnik zgodny z tablicą struktur int koniec(int) const;

public: figura (int = 1);

figura(figura&);

~figura();

void przesun (int, int, int);

void rysuj() const;

int podaj_wsp() const;

wsp operator[](int) const;

wsp& operator[](int);

friend istream& operator>>(istream&, wsp&);

friend ostream& operator<<(ostream&, const wsp);

};

(7)

#include "fig4_3.h" //plik z definicjami metod fig4_3.h ...

figura::figura (int xx)

{if (coreleft() < xx*sizeof(wsp)) exit(1);

else

{max = xx;

Figura = new wsp [max];}}

figura::figura (figura& p)

{ if (coreleft() < p.max*sizeof(wsp)) exit(1);

else

{ max = p.max;

Figura = new wsp [max];

for (int i = 0; i < max; i++) Figura[i] = p[i]; } }

figura::~figura() { delete [] Figura; } int figura::koniec(int ile) const {return ile >=0 && ile<max; }

void figura::przesun (int dx, int dy, int ile) { if (koniec(ile))

{ Figura[ile].x += dx;

Figura[ile].y += dy; } }

istream& operator>>(istream& a, figura::wsp& b) { a >> b.x >> b.y;

return a;}

ostream& operator<< (ostream& a, const figura::wsp b) { a << b.x << setw(5)<< b.y;

return a;}

figura::wsp figura::operator[](int ile) const {if (koniec(ile))

return Figura[ile];

return Figura[0]; }

figura::wsp& figura::operator[](int ile) {if (koniec(ile))

return Figura[ile];

return Figura[0]; }

(8)

3. Obiekty z dynamicznymi polami typu dwuwymiarowa tablica elementów typu int- przeciążanie operatora << na rzecz dowolnej klasy oraz operatora wywołania funkcji ()

Przykład 4

Klasa figura realizuje te same czynności, co klasa figura z przykładu 3, lecz jest wykonana w inny sposób:

·

tablica Figura jest dynamiczną tablicą o elementach typu int, traktowaną jako tablica dwuwymiarowa o liczbie wierszy max_w oraz liczbie kolumn

max_kol. Pole ile przechowuje liczbę aktualnie wprowadzonych

współrzędnych. Zastosowanie jednowymiarowej tablicy pozwala na elastyczny podział na dwa wymiary, gdzie wiersze reprezentują liczbę wierzchołków wielokąta, natomiast kolumny określają liczbę współrzędnych wierzchołków.

·

wykonano operator <<, gdzie lewy argument jest obiektem klasy figura, a prawym argument typu int (kolejne współrzędne). Wynikiem działania operatora, wykonanego jako metoda, jest referencja do lewego argumentu, czyli obiektu wywołującego metodę. Operator ten zastosowano do zapisu współrzędnych wierzchołków wielokąta do tablicy Figura.

·

zamiast metod dostępu do współrzędnych np. odcieta(int) i rzedna(int) przeciążono operator wywołania funkcji operator()(int, int) jako metodę, zastępujący indeksowanie [][] w tablicy dwuwymiarowej, niemożliwy do przeciążenia w C++ (można przeciążać tylko operator []

dwuargumentowy, gdzie lewym argumentem jest obiekt, a prawym indeks - tabela, wykład 3)

Operator wykorzystano do czytania współrzędnych wierzchołków wielokąta, umieszczonych w tablicy Figura.

Należy zabezpieczyć metody przed dostępem do elementów tablicy Figura

poza jej ograniczającymi indeksami 0 <= indeks < max_w *max_kol.

(9)

...

#include "fig4_4.h"

void wprowadz_punkty(figura&);

void wyswietl(const figura);

void main() {do

{ figura wielokat(5, 2);

wprowadz_punkty(wielokat);

wielokat.rysuj();

wyswietl(wielokat);

} while (getch() != 27); }

void wprowadz_punkty(figura& wielokat) { int ile_w = wielokat.podaj_wsp_x(), a;

int ile_kol = wielokat.podaj_wsp_y();

for (int i = 0; i < ile_w; i++) for int j = 0; j < ile_kol; j++)

{ cout << "Podaj wielokat[" << i <<",”<< j << "]:= "; cin >> a;

wielokat << a; }}

void wyswietl(const figura wielokat) { int ile_w = wielokat.podaj_wsp_x();

int ile_kol = wielokat.podaj_wsp_y();

for (int i = 0; i < ile_w; i++) {for int j = 0; j < ile_kol; j++)

cout << setw(15) << "Wielokat[" << i << "," << j << "] " <<

setw(5) << wielokat(i, j);

cout<<'\n';}}

class figura //plik nagłówkowy fig. 4_4.h

{int max_w, max_kol, ile;

int* Figura;

int koniec(int, int) const;

public: figura (int = 1, int = 2);

figura(figura&);

~figura();

void przesun (int, int, int);

void rysuj() const;

int podaj_wsp_x() const;

int podaj_wsp_y() const;

int podaj_ile() const;

figura& operator<<(int);

int operator()(int, int) const; } ;

#include "fig4_4.h" //plik z definicjami metod fig4_4.cpp ...

(10)

figura::figura (int xx, int yy)

{ if (coreleft() < xx*yy*sizeof(int)) exit(1);

else { ile = 0;

max_w = xx;

max_kol = yy

Figura = new int [max_w*max_kol]; } } figura::figura(figura& p)

{if (coreleft() < p.max_w*max_kol*sizeof(int)) exit(1);

else

{ ile = 0;

max_w = p.max_w;

max_kol = p.max_kol;

Figura = new int max_w*max_kol];

for (int i = 0; i < max_w; i++) for (int j = 0; j < max_kol; j++)

*this << p(i, j); }} // Figura[i*max_kol+j] = p(i, j);

int figura::koniec(int ile_w, int ile_kol) const

{return ile_w >=0 && ile_w<max_w && ile_kol>=0 && ile_kol<max_kol; }

figura& figura::operator<<(int xx) { if (ile < max_w*max_kol) Figura[ile++] = xx;

return (*this); }

int figura::operator()(int ile_w, int ile_kol) const {if (koniec(ile_w, ile_kol))

return Figura[ile_w*max_kol + ile_kol];

return Figura[0]; }

(11)

4. Konwersje typów definiowane przez użytkownika - przeciążanie operatora rzutowania operator typ ()

Dwa rodzaje funkcji, obowiązkowo metod (tabela z wykładu 3) umożliwiają zdefiniowanie konwersji przez użytkownika:

· konstruktory jednoargumentowe (niezależnie od rodzaju argumentu) przekształcają typ argumentu do typu klasy, do której należą

·

operatory rzutu do danego typu typ, który może być klasą lub typem podstawowym: operator typ (). Operatory te nie wymagają podania typu zwracanego wyniku. W wyniku rzutu obiekt danej klasy jest przekształcany do typu typ

Reguły do wyboru konwersji, są zbliżone do reguł stosowanych przy wywoływaniu przeciążonych funkcji:

· konwersje definiowane przez użytkownika są używane tylko wtedy, kiedy jest to niezbędne

· w jednym ciągu przekształceń (argumentu funkcji lub operatora) może być użyta tylko jedna konwersja definiowana prze użytkownika

· nie może wystąpić wiele ciągów przekształceń prowadzących do tego samego

typu

(12)

Przykład 5

Wykonano przeciążony operator konwersji operator int() dla klasy punkt.

Ciąg przekształceń musi być jednoznaczny. Gdyby przeciążono operator double(), wtedy w przypadku wyrażeń liczba_c=p1+1.25, liczba_r=p1+1,25 byłoby:

·

pierwszy ciąg przekształceń: punkt do int, int do double

·

drugi ciąg: punkt do double

#include "punkt4_5.h"

...

void wyswietl(int,char []); // 1-a funkcja prezentująca konwersję punkt do int void wyswietl(double,char []); // 2-a funkcja prezentująca konwersję punkt do int void main()

{ {punkt p1(2,2), p2(1,5);

int liczba_c;

double liczba_r;

liczba_c = p1; // p1 do int, int=int

liczba_c = p2; //p2 do int, int=int

wyswietl(p1, s2); //p1 do int, 1-a wyswietl

wyswietl(p2, s2); //p2 do int, 1-a wyswietl

liczba_c = p1+10; //p1 do int, int+int i int = int

wyswietl(p2+10, s2); //p2 do int, int+int i do 1-a wyswietl liczba_c = p1 + p2; //p1 do int i p2 do int, int+int, int=int wyswietl(liczba_r = p1, s3); //p1 do int, double=int, 2-a wyswietl liczba_r = p1 + 10; //p1 do int, int+int, double=int liczba_r = p1 + p2; //p1 do int, p2 do int, int+int, double=int

liczba_c = p1 + 1.25; //p1 do int, int + double, int=double liczba_r = p1 + 1.25; //p1 do int, int + double, double=int

liczba_r = p1; //p1 do int, double=int

} }

void wyswietl(int w, char napis[]) { cout <<napis << w << "\n"; getch(); } void wyswietl(double w, char napis[]) { cout <<napis << w << "\n"; getch();

}

class punkt //plik nagłówkowy punkt4_5.h

{ .... public:...

operator int();}; //operator konwersji obiektu klasy punkt do typu int,

#include "punkt4_5.h" //plik punkt4_5.cpp z definicjami metod

(13)

punkt::operator int () { return x + y; } // operator rzutowania int()

(14)

5. Konwersje typów definiowane przez użytkownika - konwersje z użyciem jednoargumentowych konstruktorów.

Przykład 6

Zastosowano konwersję za pomocą konstruktora jednoargumentowego typu int do klasy punkt. Wykonano przeciążony operator+ za pomocą funkcji zaprzyjaźnionej. Ciąg przekształceń musi być jednoznaczny.

...

#include "punkt4_6.h"

void wyswietl(punkt, char[]); // funkcja demonstrująca konwersję za pomocą //konstruktora

void main()

{ punkt p1(2, 2), p2(1, 5);

wyswietl(6, s2); // int do punkt (6,0) za pomocą konstruktora

wyswietl(9.9, s2); //float do int, int do punkt (9,0) za pomocą konstruktora p1 = 7; //int do punkt (7,0) za pomocą konstruktora-

//punkt tymczasowy przypisany do p1 i usunięty z wyw.

destruktora

p2 = 8.8; //double do int (8), int do punkt (8,0) za pomocą

// konstruktora -punkt tymczasowy przypisany do p1 i usunięty p1 = p2 +6; //6 do punkt za pomocą jednoargumentowego konstruktora oraz

//dodawanie p1=operator+(p2, 6)

p1 = 6 + p2; //6 do punkt za pomocą jednoargumentowego konstruktora

oraz //dodawanie p1=operator(6, p2); działanie byłoby odrzucone przez kompilator przy //realizacji operatora za pomocą metody, gdyż nie można wywołać 6.operator+

(p2) }

void wyswietl(punkt p, char napis[])

{ cout <<napis<< p.odcieta() << " "<< p.rzedna() << "\n"; getch();}...

//plik nagłówkowy punkt5_5.h class punkt

{ ...

public: punkt (int=0,int=0);

friend punkt operator +(punkt, punkt);

...};

//plik punkt5_5.cpp z definicjami metod

#include "punkt5_5.h"

...

punkt::punkt(int xx, int yy) { x = xx; y = yy; ile_punktow++; } punkt operator+(punkt p1, punkt p2)

{ return (p1.x + p2.x, p1.y + p2.y); }...

Cytaty

Powiązane dokumenty

[r]

[r]

Wykonaj następujące czynności w klasie kolo, zakładając, że znana jest definicja klasy punkt (p.1) 2.1) zdefiniuj konstruktor zwykły bez listy argumentów.. 2.2)

[r]

619.. gdy ciąg występujący pod znakiem granicy jest rozbieżny, ale nie jest to rozbieżność do +∞ ani do

Zadania domowe,

Im wartość współczynnika korelacja bardziej różni się od 0 tym siła korelacji większa. Zmienne X, Y są liniowo zależne gdy

Dwuwymiarowa zmienna losowa typu skokowego..