7146

Szczegóły
Tytuł 7146
Rozszerzenie: PDF
Jesteś autorem/wydawcą tego dokumentu/książki i zauważyłeś że ktoś wgrał ją bez Twojej zgody? Nie życzysz sobie, aby podgląd był dostępny w naszym serwisie? Napisz na adres [email protected] a my odpowiemy na skargę i usuniemy zabroniony dokument w ciągu 24 godzin.

7146 PDF - Pobierz:

Pobierz PDF

 

Zobacz podgląd pliku o nazwie 7146 PDF poniżej lub pobierz go na swoje urządzenie za darmo bez rejestracji. Możesz również pozostać na naszej stronie i czytać dokument online bez limitów.

7146 - podejrzyj 20 pierwszych stron:

prof. Jan Bielecki Visual C++ 6.0 Programowanie obiektowe Dodatki B��dy programowania Klasy pierwotne Za przyk�ad klasy pierwotnej niech pos�u�y klasa definiuj�ca poj�cie liczba zespolona. Jak wiadomo (albo nie!), liczba zespolona jest uporz�dkowan� par� liczb rzeczywistych. Do zapisu liczby zespolonej o sk�adnikach a i b u�ywa si� notacji a + bi o tak dobranym i, �e i2 = -1. St�d �atwo ju� wynika, �e (a+bi) + (c+di) == (a+c) + (b+d)i oraz �e (a+bi) * (c+di) == (a*c - b*d) + (a*d + b*c)i Sk�adnik a jest nazywany cz�ci� rzeczywist� (re), a sk�adnik b cz�ci� urojon� (im) liczby zespolonej. #include <iostream.h> struct Cplx { double re, im; }; Cplx add(Cplx &parL, Cplx parR); int main(void) { Cplx one = { 1.0, 3.0 }, two = { 2.0, 4.0 }; Cplx sum = add(one, two); char *plus = "+"; if(sum.im < 0) plus = ""; cout << sum.re << plus << sum.im << 'i' << endl; return 0; } Cplx add(Cplx &parL, Cplx parR) { double re = parL.re + parR.re, im = parL.im + parR.im; Cplx sum = { re, im }; return sum; } Definicja typu strukturowego Cplx jest opisem poj�cia liczba zespolona. Egzemplarzami typu s� zmienne one, two i sum. Program wyznacza sum� dw�ch liczb zespolonych i wyprowadza j� w postaci 3 + 7i. Hermetyzacja Przytoczone rozwi�zanie ma t� wad�, �e inicjowanie i przetwarzanie zmiennych struktury Cplx jest uci��liwe, a ka�dy u�ytkownik klasy mo�e bez ogranicze� pos�ugiwa� si� nazwami jej p�l re i im. Dlatego podczas definiowania klas stosuje si� hermetyzacj�. Polega ona na tym, �e s�owo kluczowe struct zast�puje si� s�owem class, a zestawy sk�adnik�w klasy umieszcza w sekcjach private (prywatny), public (publiczny) i protected (chroniony). Uwaga: Typy zadeklarowane z u�yciem s�owa kluczowego class s� nazywane klasami, a opisane przez nie zmienne s� nazywane obiektami. R�nica mi�dzy typami obiektowymi i strukturowymi polega jedynie na domniemaniach hermetyzacji. Dlatego ka�dy obiekt jest struktur�, a ka�da struktura jest obiektem. dla dociekliwych Ka�da deklaracja struct Any ...{ // ... }; jest r�wnowa�na deklaracji class Any ... { public: // ... }; a ka�da deklaracja class Any ...{ // ... }; jest r�wnowa�na deklaracji class Any ... { private: // ... }; a zatem r�nice mi�dzy strukturami i klasami s� niemal �adne. Podzia� na sekcje Umieszczenie sk�adnika w sekcji prywatnej oznacza, �e b�d� mog�y si� nim pos�ugiwa� tylko sk�adniki jego klasy oraz funkcje zaprzyja�nione z jego klas�. Uwaga: W celu zaprzyja�nienia funkcji z klas�, nale�y w dowolnej sekcji klasy umie�ci� deklaracj� funkcji poprzedzon� s�owem kluczowym friend. Umieszczenie sk�adnika w sekcji publicznej oznacza, �e b�d� mog�y si� nim pos�ugiwa� wszystkie funkcje i wszystkie sk�adniki. Umieszczenie sk�adnika w sekcji chronionej oznacza, �e b�d� mog�y si� nim pos�ugiwa� tylko sk�adniki jego klasy, funkcje zaprzyja�nione z jego klas� oraz sk�adniki jego klasy pochodnej. Uwaga: Umieszczenie sk�adnika (np. konstruktora albo operatora przypisania) w sekcji prywatnej, uniemo�liwia u�ywanie go poza klas�. Ten prosty zabieg funkcjonuje jak zakaz u�ywania wybranych sk�adnik�w klasy. #include <iostream.h> class Cplx { friend Cplx add(Cplx &parL, Cplx parR); protected: double re, im; public: Cplx(double r, double i) : re(r), im(i) { } void show(void) { cout << re << '+' << im << 'i' << endl; } }; Cplx add(Cplx &parL, Cplx parR) { double r = parL.re + parR.re, i = parL.im + parR.im; return Cplx(r, i); } int main(void) { Cplx one(1.0, 3.0), two(2.0, 4.0); Cplx sum = add(one, two); sum.show(); return 0; } Sk�adniki re i im s� prywatne, a sk�adniki Cplx i show s� publiczne. Funkcja show nie jest sk�adnikiem, ale jako zaprzyja�niona z klas� Cplx, mo�e odwo�ywa� si� do wszystkich jej sk�adnik�w, w tym do prywatnych sk�adnik�w re i im. Sk�adniki klasy Sk�adnikami klasy s� pola, konstruktory, destruktory i metody. Deklaracja sk�adnika mo�e by� umieszczona w sekcji prywatnej, publicznej albo chronionej. Konstruktory Konstruktorem jest sk�adnik, kt�ry s�u�y do inicjowania element�w struktury. Nazwa konstruktora jest identyczna z nazw� klasy. Deklaracja konstruktora ma posta� deklaracji funkcji, ale nie mo�e zawiera� okre�lenia typu rezultatu. class Cplx { // ... protected: double re, im; public: Cplx(double r, double i) { re = r; im = i; } Cplx(double r) { re = r; im = 0; } Cplx(void) { re = im = 0; } // ... }; Dzi�ki zdefiniowaniu konstruktor�w staj� si� poprawne nast�puj�ce deklaracje Cplx numA(3,4); Cplx numB(5); Cplx numC; Konstruktor domy�lny Konstruktor, kt�ry mo�e by� u�yty bez podania argument�w jest nazywany domy�lnym. Zazwyczaj jest nim konstruktor bezparametrowy. Uwaga: Je�li w klasie nie zdefiniuje si� ani jednego konstruktora, to jest ona niejawnie wyposa�ana w konstruktor bezparametrowy o pustym ciele. Jest on w�wczas konstruktorem domy�lnym. class Cplx { // ... protected: double re, im; public: Cplx(void) // konstrukor domy�lny { re = im = 0; } // ... }; Argumenty domniemane Konstruktory mog� mie� argumenty domniemane. Ich u�ycie upraszcza definicj� klasy. class Cplx { // ... protected: double re, im; public: Cplx(double r =0, double i =0) // konstruktor domy�lny { re = r; im = i; } // ... }; Mimo zdefiniowania tylko jednego konstruktora, s� poprawne nast�puj�ce deklaracje Cplx numA(3, 4); Cplx numB(5); Cplx numC; Lista inicjacyjna Za nag��wkiem konstruktora, po znaku : (dwukropek), wyst�puje lista inicjacyjna. Jej elementami s� napisy fld(exp, exp, ... , exp) w kt�rych fld jest identyfikatorem pola, a ka�de exp jest list� wyra�e� (zazwyczaj jednoelementow�). Opracowanie elementu listy inicjacyjnej powoduje u�ycie wyra�e� exp do zainicjowania tego elementu obiektu, kt�ry jest opisany przez pole fld. Je�li w miejscu wyst�pienia wyra�enia exp, nie jest widoczny identyfikator pola (gdy� zosta� przes�oni�ty przez identyfikator parametru konstruktora), to nazw� pola fld klasy Name jest Name::fld. Uwaga: Je�li konstruktor nie ma listy inicjacyjnej, to domniemywa si� j� pustymi wyra�eniami exp. W takim wypadku, elementy typ�w nieobiektowych (np. int) s� inicjowane warto�ciami nieokre�lonymi, a elementy typ�w obiektowych s� inicjowane przez konstruktory domy�lne. class Cplx { // ... protected: double re, im; public: Cplx(double re =0, double im =0) : re(re), im(im) { } // ... }; W inicjatorze re(re) pierwsze re jest identyfikatorem pola, a drugie jest identyfikatorem parametru. Gdyby u�yto inicjatora im(re * Cplx::re), to argumentami operacji by�aby nazwa parametru i nazwa pola. Konstruktor kopiuj�cy Konstruktorem kopiuj�cym klasy Name jest konstruktor u�ywany do inicjowania jej obiektu elementami innego obiektu klasy Name. Konstruktor kopiuj�cy jest jednoparametrowy, a jego parametr jest typu const�Name�&. Cplx numA(3, 4), numB(numA), numC = numB; Je�li definicja klasy nie zawiera konstruktora kopiuj�cego, to jest niejawnie uzupe�niana definicj� takiego publicznego konstruktora, kt�ry inicjuje elementy obiektu elementami obiektu inicjuj�cego. Oznacza to, �e dla klasy Cplx domniemany konstruktor kopiuj�cy jest zdefiniowany przez funkcj� Cplx(const Cplx &par) : re(par.re), im(par.im) { } Uwaga: Inicjowanie realizowane przez domniemany konstruktor kopiuj�cy polega na kopiowaniu-p�ytkim. Je�li klasa zawiera pola wska�nikowe lub odno�nikowe, to kopiowanie-p�ytkie mo�e okaza� si� niezadowalaj�ce. W takim wypadku nale�y zdefiniowa� w�asny konstruktor kopiuj�cy, kt�ry wykona kopiowanie-g��bokie (uwzgl�dniaj�ce klonowanie zmiennych identyfikowanych przez wska�niki i odno�niki). class Cplx { // ... protected: double re, im; public: Cplx(double re =0, double im =0) : re(re), im(im) { } Cplx(const Cplx &par) : re(par.re), im(par.im) { } // ... }; U�ycie w�asnego konstruktora kopiuj�cego jest zbyteczne, poniewa� domniemany konstruktor kopiuj�cy dla klasy Cplx ma dok�adnie tak� sam� definicj�. dla dociekliwych Mo�e powsta� pytanie, kiedy do nadawania warto�ci pocz�tkowych elementom obiektu u�ywa� listy inicjacyjnej, a kiedy u�ywa� instrukcji w ciele konstruktora. Je�li pola s� typu nieobiektowego (np. int albo char *), to skutek jest taki sam. Ale je�li elementy obiektu s� typu obiektowego, to r�nica mo�e by� istotna. Wynika to st�d, �e nadawanie warto�ci za pomoc� listy ma semantyk� inicjowania, a nadawanie warto�ci za pomoc� instrukcji, ma semantyk� przypisywania. Dlatego zaleca si� u�ywanie listy inicjacyjnej. Destruktory Destruktorem jest sk�adnik, kt�ry s�u�y do wykonania czynno�ci poprzedzaj�cych zniszczenie obiektu. Nazw� destruktora jest po��czenie znaku ~ (tylda) i nazwy klasy. Deklaracja konstruktora ma posta� deklaracji funkcji, ale nie mo�e zawiera� okre�lenia typu rezultatu. Destruktor jest bezparametrowy i nie mo�e by� przeci��ony. Uwaga: Destruktor jest wywo�ywany niejawnie. Mo�na go wywo�a� jawnie, ale w�wczas nale�y naprawd� dobrze wiedzie� co si� robi. Dlatego taki spos�b post�powania lepiej zostawi� zawodowcom. #include <iostream.h> class Cplx { friend Cplx add(Cplx &parL, Cplx parR); protected: double re, im; public: Cplx(double re, double im) : re(re), im(im) { cout << "Constructing: ", show(); } ~Cplx(void) { cout << "Destructing: ", show(); } void show(void) { cout << re << '+' << im << 'i' << endl; } }; int main(void) { Cplx numA(1, 2); { Cplx numB(3, 4); // ... } return 0; } Wykonanie programu powoduje wyprowadzenie napisu Constructing: 1+2i Constructing: 3+4i Destructing: 3+4i Destructing: 1+2i Potwierdza si� fakt, �e obiekty automatyczne s� niszczone w kolejno�ci odwrotnej do kolejno�ci ich tworzenia. Metody Je�li sk�adnikiem klasy Name jest metoda fun, a obiektem tej klasy wskazanym przez ptr jest obj, to ptr->fun(arg, arg, ... , arg) oraz obj.fun(arg, arg, ... , arg) jest wywo�aniem metody fun na rzecz obiektu obj. Uwaga: Metoda mo�e by� wywo�ana tylko na rzecz obiektu. Je�li wyra�enie *ptr albo obj nie jest nazw� obiektu, to nie poddaje si� go niejawnej konwersji. Dlatego poprawna mo�e by� m.in. operacja obj�+�"!", ale nie jest poprawna operacja "!"�+�obj, chyba �e operator + zdefiniowano za pomoc� funkcji globalnej. W chwili wywo�ania metody jest tworzony wska�nik this typu Name�*, zainicjowany wskazaniem obiektu obj. Podczas wykonywania metody fun, trwa�� nazw� tego obiektu jest *this, a nazw� jego sk�adnika m jest (*this).m albo pro�ciej: this->m. Uwaga: Je�li w miejscu wyst�pienia this->m jest widoczna deklaracja sk�adnika m, to this->m mo�na upro�ci� do m. #include <iostream.h> class Cplx { protected: double re, im; public: Cplx(double r, double i) : re(r), im(i) { } Cplx add(Cplx &par) { double re = this->re + par.re, im = this->im + par.im; return Cplx(re, im); } void show(void) { cout << this->re << '+' << this->im << 'i' << endl; } }; int main(void) { Cplx one(1.0, 3.0), two(2.0, 4.0); Cplx sum = one.add(two); sum.show(); // 3+7i return 0; } W ciele funkcji add obiekt one ma nazw� *this, a jego elementy maj� nazwy this->re i this-im. Funkcje operatorowe Funkcj� operatorow� jest funkcja o nazwie operator@ w kt�rej @ jest jednym operator�w wymienionych w tabeli Operatory. Tabela Operatory new delete + - * / % ^ & | ~ ! = < > += -= *= /= %= ^= &= |= << >> >>= <<= == != <= >= && || ++ -- , () [] -> ->* Funkcja operatorowa mo�e by� funkcj� globaln� albo metod� klasy. Operator mo�e by� zdefiniowany przez funkcj� globaln� tylko w�wczas, gdy ma ona co najmniej jeden argument obiektowy. Operatory =, [], () i -> mog� by� definiowane tylko przez metody. Operatory jednoargumentowe Je�li w pewnym miejscu programu wyst�puje operacja @a w kt�rej a jest wyra�eniem typu obiektowego, to jest traktowana jak wywo�anie operator@(a) jednoparametrowej funkcji globalnej operator@. Je�li takiej funkcji nie ma, to jest traktowana jak wywo�anie a.operator@() bezparametrowej metody nale��cej do klasy obiektu a. Je�li w pewnym miejscu programu wyst�puje operacja a-- albo a++ to, dla odr�nienia od operacji poprzednikowej, jest traktowana tak, jak wywo�anie funkcji z dodatkowym parametrem typu int. Uwaga: Je�li istnieje funkcja globalna, to zabrania si�, aby w klasie obiektu a istnia�a metoda bezparametrowa. #include <iostream.h> class Cplx { friend Cplx operator~(Cplx &par); friend void operator++(Cplx &par, int); friend ostream &operator<<(ostream &out, Cplx &par); protected: double re, im; public: Cplx(double r, double i) : re(r), im(i) { } Cplx operator-(void) { return Cplx(-re, -im); } void operator++(void) { ++re; } }; Cplx operator~(Cplx &par) { return Cplx(par.re, -par.im); } void operator++(Cplx &par, int) { ++par.im; } ostream &operator<<(ostream &out, Cplx &par) { if(par.re) out << par.re; if(par.re && par.im > 0) out << '+'; return out << par.im << 'i'; } int main(void) { Cplx one(1,2); cout << ~-one << endl; // -1+2i Cplx two(3,4); ++two; two++; cout << two << endl; // 4+5i return 0; } Operacja -one jest traktowana jak wywo�anie operator-(one) i jest realizowana przez funkcj� globaln�, zaprzyja�nion� z klas�. Operacja ~-one jest traktowana jak wywo�anie one.operator<<(-one) i jest realizowana przez metod� klasy. Operacja ++two zwi�ksza o 1 warto�� cz�ci rzeczywistej, a operacja two++ zwi�ksza o 1 warto�� cz�ci urojonej. Operacja ++two jest traktowana tak, jak wywo�anie two.operator++(), a operacja two++ jest traktowana tak, jak wywo�anie operator ++(two,�0). Operatory dwuargumentowe Je�li w pewnym miejscu programu wyst�puje operacja a @ b w kt�rej a albo b jest wyra�eniem typu obiektowego, to jest traktowana jak wywo�anie operator@(a, b) dwuparametrowej funkcji globalnej operator@. Je�li takiej funkcji nie ma, to jest traktowana jak wywo�anie a.operator@(b) jednoparametrowej metody nale��cej do klasy obiektu a. Uwaga: Je�li istnieje funkcja globalna, to zabrania si�, aby w klasie obiektu a istnia�a metoda, kt�r� mo�na by wywo�a� z argumentem b. #include <iostream.h> class Cplx { friend ostream &operator<<(ostream &out, Cplx &par); protected: double re, im; public: Cplx(double r, double i) : re(r), im(i) { } Cplx operator+(Cplx &par) { double re = Cplx::re + par.re, im = Cplx::im + par.im; return Cplx(re, im); } }; ostream &operator<<(ostream &out, Cplx &par) { if(par.re) out << par.re; if(par.re && par.im > 0) out << '+'; return out << par.im << 'i'; } int main(void) { Cplx one(1.0, 3.0), two(2.0, 4.0); Cplx sum = one + two; cout << sum << endl; // 3+7i return 0; } Operacja one�+�two jest traktowana jak wywo�anie one.operator+(two) i jest realizowana przez metod� klasy. Operacja cout�<<�sum jest traktowana jak wywo�anie operator<<(cout,�sum) i jest realizowana przez funkcj� globaln� zaprzyja�nion� z klas�. Operatory specjalne Niekt�re operatory musz� by� implementowane przez metody. S� nimi =�(przypisanie), []�(indeksowanie), ->�(wybranie) i () (wywo�anie). Ostatni z nich mo�e by� wieloargumentowy. Je�li w klasie nie zdefiniowano operatora przypisania, to jest domniemywany w postaci publicznej metody, realizuj�cej kopiowanie-p�ytkie. Uwaga: Rezultatem domniemanego przypisania jest odno�nik do lewego argumentu. Oznacza to, �e dla klasy Cplx domniemana operacja przypisania jest zdefiniowana przez funkcj� Cplx &operator=(const Cplx &par) { re = par.re; im = par.im; return *this; } Uwaga: Inicjowanie realizowane przez domniemany operator przypisania polega na kopiowaniu-p�ytkim. Je�li klasa zawiera pola wska�nikowe lub odno�nikowe, to mo�e okaza� si� niezadowalaj�ce. W takim wypadku nale�y zdefiniowa� w�asny operator przypisania, kt�ry wykona kopiowanie-g��bokie (uwzgl�dniaj�ce klonowanie zmiennych identyfikowanych przez wska�niki i odno�niki). Konwertery Konwerterem jest metoda, kt�ra definiuje konwersj� obiektu jej klasy na obiekt typu docelowego. Je�li w pewnym miejscu wyst�puje operacja (Type)a w kt�rej a wyra�eniem typu obiektowego, a Type jest nazw� typu docelowego, to jest traktowana jak wywo�anie a.operator Type() bezparametrowej metody operator Type zdefiniowanej w klasie obiektu a. #include <iostream.h> #include <math.h> class Cplx { friend ostream &operator<<(ostream &out, Cplx &par); protected: double re, im; public: Cplx(double r, double i) : re(r), im(i) { } operator double(void) { return sqrt(re * re + im * im); } }; ostream &operator<<(ostream &out, Cplx &par) { if(par.re) out << par.re; if(par.re && par.im > 0) out << '+'; return out << par.im << 'i'; } int main(void) { Cplx one(3,4); cout << one << endl; // 3+4i cout << (double)one << endl; // 5 return 0; } Operacja (double)one jest traktowana jak wywo�anie one.operator�double(). Niejawne konwersje W ka�dym miejscu, gdzie typ wyra�enia jest niezgodny z typem inicjowanej nim zmiennej, mo�e by� zastosowana niejawna konwersja standardowa, konstruktorowa albo konwerterowa. Konwersja konstruktorowa jest okre�lona przez konstruktor, a konwerterowa przez konwerter. Wymaga si�, aby zastosowana konwersja by�a jednoznaczna. #include <iostream.h> class Cplx { private: double re, im; public: Cplx(double re =0, // =(double)0 double im =0) // =(double)0 : re(re), im(im) { } operator double(void) { return re; // (double)re } }; Cplx num = 12; // Cplx(12) int main(void) { cout << num << endl; // num.operator double() return 0; } Sk�adniki statyczne Sk�adnik statyczny klasy jest zadeklarowany ze specyfikatorem static. Jest on w istocie elementem klasy, a nie obiektu i istnieje nawet w�wczas, gdy nie utworzono ani jednego obiektu klasy. Nazw� statycznego sk�adnika mem klasy Class jest Class::mem. Funkcja statyczna nie ma wska�nika this, a wi�c nie mo�e odwo�ywa� si� do p�l nie-statycznych. Pole statyczne nale�y zadeklarowa� w klasie i zdefiniowa� poza klas� (obecnie tak�e i w klasie). Domy�lnym inicjatorem pola statycznego jest =�0. #include <iostream.h> class Real { private: static int count; // deklaracja protected: double re; public: Real(double re =0) : re(re) { count++; } ~Real(void) { count--; } static int getCount(void) { return count; } }; int Real::count = 0; // definicja int main(void) { cout << Real::getCount() << endl; // 0 Real r1; { Real r2, r3; cout << Real::getCount() << endl; // 3 } cout << Real::getCount() << endl; // 1 return 0; } Metoda statyczna getCount dostarcza informacji o liczbie obiekt�w klasy Real. Dystrybucja klasy Przetestowana definicja klasy, uzupe�niona o wspomagaj�ce j� funkcje globalne jest dystrybuowana w postaci �r�d�owego pliku nag��wkowego i binarnego pliku implementacyjnego. U�ytkownik klasy w��cza do swoich modu��w nag��wek klasy i do��cza do swojego projektu plik implementacyjny. Klasa Cplx Przytoczona tu klasa zawiera przyk�adowy zestaw sk�adnik�w i funkcji wspomagaj�cych. Poniewa� sk�adniki funkcyjne zdefiniowane w ciele klasy s� domy�lnie otwarte (inline), wi�c aby uczyni� je zamkni�tymi mo�na je zdefiniowa� poza cia�em klasy. Uwaga: Identyfikator sk�adnika zdefiniowanego poza cia�em klasy musi by� poprzedzony kwalifikatorem zakresu Name::, w kt�rym Name jest nazw� jego klasy. #include <iostream.h> #include <math.h> class Cplx { friend inline double sqrt(Cplx &par); protected: double re, im; public: Cplx(double re =0, double im =0) : re(re), im(im) { } ~Cplx(void) { } operator double(void) { return sqrt(re * re + im * im); } Cplx operator+(Cplx &par); Cplx operator~(void) { return Cplx(re, -im); } void putCplx(ostream &out) { if(re) out << re; if(re && im > 0) out << '+'; out << im << 'i'; } void getCplx(istream &inp) { inp >> re; if(inp.peek() == 'i') im = re, re = 0; else { inp >> im; if(inp.peek() != 'i') { inp.clear(ios::badbit); return; } } char drop; inp >> drop; } }; Cplx Cplx::operator+(Cplx &par) { return Cplx(re + par.re, im + par.im); } double sqrt(Cplx &par) { return (double)par; } ostream &operator<<(ostream &out, Cplx &par) { par.putCplx(out); return cout; } istream &operator>>(istream &inp, Cplx &par) { par.getCplx(inp); return inp; } int main(void) { Cplx one, two; cin >> one >> two; cout << "Sum = " << one + two << endl; cout << "sqrt(" << one << ") = " << double(one) << endl; cout << "sqrt(" << two << ") = " << sqrt(two) << endl; return 0; } Aby nie zaprzyja�nia� funkcji operatorowych operator<< i operator>> z klas� Cplx u�yto w nich publicznych metod putCplx i getCplx. Plik nag��wkowy W pliku nag��wkowym pozostawia si� deklaracje jej sk�adnik�w i funkcji wspomagaj�cych oraz definicje sk�adnik�w i funkcji wspomagaj�cych zadeklarowanych (jawnie albo niejawnie) ze specyfikatorem inline. W nag��wku pozostawia si� argumenty domniemane, ale usuwa si� z niego listy inicjacyjne (wraz z dwukropkami). Uwaga: Deklaracje nag��wka obudowuje si� zazwyczaj dyrektywami #ifndef, #define i #endif. Dzi�ki temu unika si� b��d�w spowodowanych wi�cej ni� jednokrotnym w��czeniem nag��wka do tego samego pliku �r�d�owego. #ifndef CPLX #define CPLX #include <iostream.h> #include <math.h> class Cplx { friend inline double sqrt(Cplx &par); protected: double re, im; public: Cplx(double re =0, double im =0); ~Cplx(void); operator double(void); Cplx operator+(Cplx &par); Cplx operator~(void); void putCplx(ostream &out); void getCplx(istream &inp); }; double sqrt(Cplx &par); ostream &operator<<(ostream &out, Cplx &par); istream &operator>>(istream &inp, Cplx &par); #endif Plik implementacyjny Plik implementacyjny zawiera dyrektyw� #include w��czaj�c� nag��wek oraz definicje tych wszystkich funkcji, kt�re zadeklarowano (tylko zadeklarowano!) w nag��wku. W pliku implementacyjnym pozostawia si� listy inicjacyjne i specyfikatory virtual, ale usuwa si� z niego argumenty domniemane. #include "cplx.h" #include <iostream.h> #include <math.h> Cplx::Cplx(double re, double im) : re(re), im(im) { } Cplx::~Cplx(void) { } Cplx::operator double(void) { return sqrt(re * re + im * im); } Cplx Cplx::operator~(void) { return Cplx(re, -im); } void Cplx::putCplx(ostream &out) { if(re) out << re; if(re && im > 0) out << '+'; out << im << 'i'; } void Cplx::getCplx(istream &inp) { inp >> re; if(inp.peek() == 'i') im = re, re = 0; else { inp >> im; if(inp.peek() != 'i') { inp.clear(ios::badbit); return; } } char drop; inp >> drop; } Cplx Cplx::operator+(Cplx &par) { return Cplx(re + par.re, im + par.im); } double sqrt(Cplx &par) { return (double)par; } ostream &operator<<(ostream &out, Cplx &par) { par.putCplx(out); return cout; } istream &operator>>(istream &inp, Cplx &par) { par.getCplx(inp); return inp; } Program testuj�cy #include <iostream.h> #include <math.h> #include "cplx.h" int main(void) { Cplx one, two; cin >> one >> two; cout << "Sum = " << one + two << endl; cout << "sqrt(" << one << ") = " << double(one) << endl; cout << "sqrt(" << two << ") = " << sqrt(two) << endl; return 0; } Klasy pochodne Klas� pochodn� jest klasa, kt�ra wywodzi si� od klasy bazowej wyszczeg�lnionej na li�cie dziedziczenia.. Klas� pochodn� tworzy si� w�wczas, gdy jej klasie bazowej brakuje pewnych sk�adnik�w, ale gdy nowej klasy nie chce si� definiowa� od pocz�tku. class Derived : public Base1, public Base2 { // ... }; Klasa Derived pochodzi od klas Base1 i Base2 wyszczeg�lnionych na li�cie dziedziczenia. Dziedziczenie i inicjowanie Klas pochodna dziedziczy wszystkie sk�adniki klasy bazowej, ale nie dziedziczy konstruktor�w i operator�w przypisania. Ka�dy obiekt klasy pochodnej sk�ada si� z takich samych element�w z jakich sk�ada si� obiekt klasy bazowej oraz z tych dodatkowych element�w, kt�re s� opisane przez pola klasy pochodnej. Inicjowanie element�w klasy bazowej odbywa si� za pomoc� konstruktora klasy bazowej, wywo�anego z listy inicjacyjnej konstruktora klasy pochodnej. Uwaga: T� cz�� obiektu klasy pochodnej, kt�ra jest obiektem klasy bazowej nazywa si� podobiektem klasy pochodnej. class Real { protected: double re; public: Real(double re) : re(re) { } }; class Cplx : public Real { protected: double im; public: Cplx(double re, double im) : Real(re), im(im) { } }; Obiekt klasy Cplx sk�ada si� z element�w opisanych przez pole re klasy Real i pole im klasy Cplx. Element listy inicjacyjnej Real(re) s�u�y do zainicjowania tej cz�ci obiektu klasy Cplx, kt�ra jest podobiektem klasy Real. Identyfikowanie podobiekt�w Je�li klasa Derived wywodzi si� od klasy Primary, a obiekt objD klasy Derived zawiera obiekt objP klasy Primary, to (Primary &)objD jest nazw� podobiektu objP (Derived &)objP jest nazw� obiektu objD (Primary *)&objD jest wska�nkiem na podobiekt objP (Derived *)&objP jest wska�nikiem na obiekt objD *(Primary *)&objD jest nazw� podobiektu objP *(Derived *)&objP jest nazw� obiektu objD Uwaga: Konwersja odno�nika-do-obiektu na odno�nik-do-jego-podobiektu oraz konwersja wska�nika-na-obiekt we wska�nik-na-podobiekt jest konwersj� standardow� i jako taka mo�e by� wykonana niejawnie. #include <iostream.h> class Real { friend ostream &operator<<(ostream &out, Real &par); private: double *pRe; public: Real(double re =0) : pRe(new double) { *pRe = re; } Real(const Real &par) : pRe(new double) { *pRe = *par.pRe; } ~Real(void) { delete pRe; } Real &operator=(Real &par) { *pRe = *par.pRe; return *this; } }; ostream &operator<<(ostream &out, Real &par) { return out << *par.pRe; } class Cplx : public Real { friend ostream &operator<<(ostream &out, Cplx &par); private: double *pIm; public: Cplx(double re, double im) : Real(re), pIm(new double) { *pIm = im; } Cplx(const Cplx &par) : Real(par), pIm(new double) { *pIm = *par.pIm; } Cplx(void) { delete pIm; } Cplx &operator=(Cplx &par) { (Real &)*this = (Real &)par; *pIm = *par.pIm; return *this; } }; ostream &operator<<(ostream &out, Cplx &par) { out << (Real &)par; if(par.re && *par.pIm > 0) out << '+'; return out << *par.pIm << 'i'; } int main(void) { Cplx num(3,4); cout << num << endl; // 3+4i return 0; } W wyra�eniu (Real�&)*this�=�(Real�&)par napis (Real�&)*this jest nazw� podobiektu obiektu *this, a napis (Real &)par jest nazw� podobiektu obiektu par. Pierwszy z tych napis�w jest r�wnowa�ny napisowi *(Real�*)this, a drugi jest r�wnowa�ny napisowi *(Real�*)&par. Zast�pienie rozpatrywanego wyra�enia wyra�eniem *pRe�=�*par.pRe jest niemo�liwe, poniewa� pole pRe jest prywatne, a metoda operator= klasy Cplx nie jest zaprzyja�niona z klas� Real. Gdyby nie zdefiniowano funkcji operator<< dla klasy Cplx, to operacja cout�<<�num zosta�aby niejawnie zast�piona operacj� cout�<<�(Real�&)num i zosta�aby wyprowadzona liczba 3. dla dociekliwych Po utworzeniu obiektu i jego podobiekt�w jest wywo�ywany konstruktor obiektu. Podczas opracowywania jego listy inicjacyjnej s� wywo�ywane konstruktory podobiekt�w. Nast�pnie odbywa si� inicjowanie w�asnych element�w obiektu. Na zako�czenie jest wykonywane cia�o konstruktora obiektu. Uwaga: Inicjowanie podobiekt�w odbywa si� w kolejno�ci wyszczeg�lnienia klas na li�cie dziedziczenia. Inicjowanie w�asnych element�w obiektu odbywa si� w kolejno�ci zadeklarowania ich p�l. Tu� przed zniszczeniem obiektu jest wywo�ywany destruktor obiektu. Po wykonaniu jego cia�a s� wywo�ywane destruktory w�asnych element�w obiektu, a nast�pnie destruktory podobiekt�w. Uwaga: Destruktory element�w s� wywo�ywane w kolejno�ci odwrotnej do zadeklarowania ich p�l. Destruktory podobiekt�w s� wywo�ywane w kolejno�ci odwrotnej do wyszczeg�lnienia klas na li�cie dziedziczenia. #include <iostream.h> class Real { private: double re; public: Real(double re) : re(re) { cout << "Real-C: " << re << endl; } ~Real(void) { cout << "Real-D: " << re << endl; } }; class Cplx : public Real { private: Real im; public: Cplx(double re, double im) : im(im), Real(re) { cout << "Cplx-C: " << endl; } ~Cplx(void) { cout << "Cplx-D: " << endl; } }; int main(void) { Cplx num(3,4); cout << endl; return 0; } Wykonanie programu powoduje wyprowadzenie napisu Real-C: 3 Real-C: 4 Cplx-C: Cplx-D: Real-D: 4 Real-D: 3 Potwierdza si�, �e kolejno�� element�w listy inicjacyjnej jest nieistotna. Studium programowe Je�li kto� dysponuje klas� Cplx, z definicj� zawart� w nag��wku cplx.h i implementacj� w pliku binarnym cplx.obj (por. poprzedni rozdzia�), ale chce mie� klas� do niej podobn�, kt�rej ka�dy obiekt zawiera dodatkowy element, okre�laj�cy ile razy cz�� urojona mia�a warto�� 0, to taka klas�, nazwan� tu Cplx2, mo�e zdefiniowa� nast�puj�co. #include "cplx.h" class Cplx2 : public Cplx { friend ostream &operator<<(ostream &out, Cplx2 &par); protected: int count; public: Cplx2(double re =0, double im =0) : Cplx(re, im), count(0) { } Cplx2 &operator=(const Cplx2 &par) { if(par.im == 0) count++; (Cplx &)*this = par; return *this; } }; ostream &operator<<(ostream &out, Cplx2 &par) { out << (Cplx &)par; if(par.count != 0) out << ':' << par.count; return out; } istream &operator>>(istream &inp, Cplx2 &par) { inp >> (Cplx &)par; if(inp.peek() == ':') { char chr; int num; inp >> chr >> num; } return inp; } Podobnie jak uczyniono to uprzednio, klas� Cplx2 mo�na dystrybuowa� w postaci pliku nag��wkowego cplx2.h i pliku implementacyjnego cplx2.obj. Plik nag��wkowy #ifndef CPLX2 #define CPLX2 #include <iostream.h> #include "cplx.h" class Cplx2 : public Cplx { friend ostream &operator<<(ostream &out, Cplx2 &par); protected: int count; public: Cplx2(double re =0, double im =0); Cplx2 &operator=(const Cplx2 &par); }; ostream &operator<<(ostream &out, Cplx2 &par); istream &operator>>(istream &inp, Cplx2 &par); #endif Plik implementacyjny #include "cplx2.h" #include <iostream.h> Cplx2::Cplx2(double re, double im) : Cplx(re, im), count(0) { } Cplx2 &Cplx2::operator=(const Cplx2 &par) { if(par.im == 0) count++; (Cplx &)*this = par; return *this; } ostream &operator<<(ostream &out, Cplx2 &par) { out << (Cplx &)par; if(par.count != 0) out << ':' << par.count; return out; } istream &operator>>(istream &inp, Cplx2 &par) { inp >> (Cplx &)par; if(inp.peek() == ':') { char chr; int num; inp >> chr >> num; } return inp; } Program testuj�cy #include <iostream.h> #include "cplx2.h" int main(void) { Cplx2 num(3,4); cout << num << endl; // 3+4i num = 0; num = Cplx2(1,2); num = 0; cin >> num; // 5i cout << num << endl; // 5i:2 return 0; } Metody wirtualne Metod� wirtualn� jest metoda zadeklarowana ze specyfikatorem virtual. Je�li wywo�anie metody wirtualnej odbywa si� na rzecz obiektu kompletnego (a nie na rzecz jego podobiektu!), albo zawiera operator zakresu (np. obj.Cplx::abs()), to skutek wywo�ania b�dzie taki sam jak dla metody nie-wirtualnej. Je�li wywo�anie odbywa si� na rzecz podobiektu obiektu kompletnego, to zostanie wykonana metoda o r�wnowa�nej sygnaturze, widoczna w klasie obiektu kompletnego. Taki spos�b wywo�ania jest polimorficzny, poniewa� jest inny podczas kompilowania, a inny podczas wykonania programu. Uwaga: Sygnatur� funkcji uzyskuje si� po usuni�ciu z jej deklaracji specyfikator�w typu funkcji, identyfikator�w jej parametr�w i opis�w argument�w domniemanych. W szczeg�lno�ci funkcja o deklaracji const�int�fun(int par[3]) ma sygnatur� fun(int�[3]). #include <iostream.h> #include <math.h> class Real { protected: double re; public: Real(double re) : re(re) { } virtual double abs(void) { return re < 0 ? -re : re; } }; class Cplx : public Real { protected: double im; public: Cplx(double re, double im) : Real(re), im(im) { } double abs(void) { return sqrt(re * re + im * im); } }; int main(void) { Cplx num(3,4); Cplx &ref = num; Cplx *ptr = &num; cout << num.abs() << endl; // 5 cout << ref.abs() << endl; // 5 cout << ptr->abs() << endl; // 5 return 0; } Wywo�anie num.abs() odbywa si� na rzecz obiektu kompletnego, wi�c zostanie wywo�ana metoda abs jego klasy, czyli Cplx::abs. Wywo�anie ref.abs() odbywa si� na rzecz podobiektu klasy Real, identyfikowanego przez ref. Poniewa� w klasie Real metoda abs jest wirtualna, wi�c faktycznie zostanie wywo�ana metoda abs widoczna w klasie obiektu kompletnego do kt�rego nale�y podobiekt, czyli Cplx::abs. Wywo�anie ptr->abs() odbywa si� na rzecz podobiektu *ptr klasy Real, wskazanego przez ptr. Poniewa� w klasie Real metoda abs jest wirtualna, wi�c faktycznie zostanie wywo�ana metoda abs widoczna w klasie obiektu kompletnego, kt�rego podobiektem jest *ptr, czyli Cplx::abs. Gdyby z definicji funkcji Real::abs usuni�to specyfikator virtual, to wywo�ania ref.abs() i ptr->abs() dotyczy�yby metody Real::abs, co spowodowa�oby wyprowadzenie liczb 4. Gdyby z klasy Cplx usuni�to metod� abs, to w klasie obiektu kompletnego by�aby widoczna metoda abs odziedziczona z klasy Real, a wi�c w ka�dym z rozpatrzonych przypadk�w zosta�aby wywo�ana metoda Real::abs. Czyste metody wirtualne Je�li nie przewiduje si� wykonania metody wirtualnej, to mo�na usun�� jej cia�o, a w deklaracji umie�ci� inicjator =�0. Tak zdefiniowana metoda jest czyst� metod� wirtualn�. virtual double abs(void) = 0; Klasa, kt�ra zawiera przynajmniej jedn� czyst� metod� wirtualn� jest klas� abstrakcyjn�. Klasa abstrakcyjna jest przydatna tylko do definiowania klas pochodnych (nie mo�na tworzy� obiekt�w takiej klasy). Je�li w klasie pochodnej klasy abstrakcyjnej nie zdefiniuje si� wszystkich odziedziczonych przez ni� czystych metod wirtualnych, to taka klasa tak�e b�dzie klas� abstrakcyjn�. Studium programowe Studium po�wi�cono zdefiniowaniu klasy z metod� wirtualn� oraz klasy pochodnej. Pokazano, jak nie zmieniaj�c pierwotnej definicji operatora wyj�cia mo�na, dzi�ki przedefiniowaniu metody wirtualnej, zmieni� spos�b wyprowadzania wynik�w. Klasa String Niech b�dzie dana nast�puj�ca klasa String do wykonywania operacji na �a�cuchach o rozmiarze nie przekraczaj�cym 256 element�w. #include <iostream.h> #include <string.h> class String { friend ostream &operator<<(ostream &out, String &par); private: int len; char str[256]; public: String(char *ptr) : len(strlen(ptr)) { strcpy(str, ptr); } String &operator+=(char *ptr) { strcpy(str + len, ptr); return *this; } String &operator+=(String &par) { return *this += par.str; } virtual char operator[](int pos) { return str[pos]; } }; ostream &operator<<(ostream &out, String &par) { for(int i = 0; i < par.len ; i++) out << par[i]; return out; } U�ycie Je�li klas� String jest dystrybuowana w postaci pliku nag��wkowego string.h i pliku implementacyjnego string.obj, to mo�na jej u�y� w nast�puj�cym programie. #include <iostream.h> #include "string.h" int main(void) { String h("Hello"), w("World"); cout << h << endl; // Hello cout << (h += w) << endl; // HelloWorld cout << ((h += " ") += w) << endl; // Hello World return 0; } Operacja += musi by� uj�ta w nawiasy. Wynika to st�d, �e jej priorytet jest ni�szy od priorytetu operacji <<. Klasa String2 Je�li u�ytkownik klasy String nie jest zadowolony ze sposobu wykonywania operacji wyj�cia, to korzystaj�c z tego, �e metoda operator[] jest wirtualna, mo�e zdefiniowa� nast�puj�c� klas� String2 i przedefiniowa� w niej t� metod� tak, aby dostarcza�a nie kody liter �a�cucha, ale kody odpowiadaj�cych im du�ych liter. #include <ctype.h> class String2 : public String { public: String2(char *ptr) : String(ptr) { } char operator[](int pos) { return toupper(String::operator[](pos)); } }; Uwaga: Argumentowi funkcji toupper nie nadano postaci str(pos), poniewa� identyfikator str jest prywatny, a wi�c w klasie String2 jest niedost�pny. Nie nadano mu postaci ((String�&)*this)[pos], r�wnowa�nej (*this)[pos], poniewa� spowodowa�oby to rekurencyjne wywo�anie metody String2::operator[], to jest wpadni�cie programu w p�tl�. U�ycie Je�li klas� String2 jest dystrybuowana w postaci pliku nag��wkowego string2.h i pliku implementacyjnego string2.obj, to mo�na jej u�y� w nast�puj�cym programie. #include <iostream.h> #include "string2.h" int main(void) { String2 h("Hello"), w("World"); cout << h << endl; // HELLO cout << (h += w) << endl; // HELLO WORLD cout << ((h += " ") += w) << endl; // HELLO WORLD return 0; } Uzasadnienie Definicja klasy String ma posta� class String { // ... virtual char operator[](int pos) { return str[pos]; } }; ostream &operator<<(ostream &out, String &par) { for(int i = 0; i < par.len ; i++) out << par[i]; return out; } a definicja klasy String2 ma posta� class String2 : public String { // ... char operator[](int pos) { return toupper(String::operator[](pos)); } }; Poniewa� nie zdefiniowano operatora wyj�cia dla obiekt�w klasy String2, wi�c w zasi�gu deklaracji String2 h("Hello"); wykonanie operacji cout << h powoduje niejawne zastosowanie konwersji standardowej odno�nika-do-obiektu na odno�nik-do-podobiektu cout << (String &)h a nast�pnie wywo�anie operatora wyj�cia dla obiekt�w klasy String. W ciele tego operatora parametr par jest odno�nikiem do podobiektu, a wi�c wywo�anie par[i] metody wirtualnej operator[] klasy String zostaje zast�pione wywo�aniem metody operator[] klasy String2. A zatem w miejscu wywo�ania par[i] jest dostarczany kod du�ej litery. Projektowanie kolekcji Klas� kolekcyjn� jest klasa, kt�rej obiekty s� przystosowane do przechowywania obiekt�w wybranej rodziny klas. Zazwyczaj stawia si� wymaganie, aby klasy rodziny wywodzi�y si� od wsp�lnej klasy pierwotnej. Klas� iteracyjn� jest klasa, kt�ra umo�liwia otrzymywanie wska�nik�w albo odno�nik�w na kolejne obiekty znajduj�ce si� w kolekcji. Klasa iteracyjna jest zazwyczaj definiowana jako klasa wewn�trzna jej klasy kolekcyjnej. W nast�puj�cym programie zdefiniowano klas� kolekcyjn� Numbers umo�liwiaj�c� tworzenie kolekcji oraz klas� iteracyjn� Numbers::Scanner umo�liwiaj�c� przegl�danie kolekcji. Klasa Numbers umo�liwia dodawanie obiekt�w do kolekcji (operator+=) oraz ujawnianie i wyznaczanie sumy warto�ci bezwzgl�dnych wszystkich obiekt�w znajduj�cych si� w kolekcji (metody showAll i getSum). W kolekcji mo�na przechowywa� obiekty numeryczne dowolnych klas pochodnych od klasy Root, byle tylko ka�d� z nich wyposa�ono w metody wirtualne showVal i getAbs. Uwaga: Klas� Numbers zdefiniowano w taki spos�b, �e �adna z jej metod nie zale�y od typu obiekt�w umieszczanych w kolekcji. W�a�ciwo�� ta umo�liwia umieszczanie w kolekcjach Numbers, obiekt�w takich klas, kt�re nie by�y znane podczas definiowania klasy kolekcyjnej. #include <iostream.h> #include <math.h> #include <stdlib.h> class Root { friend class Numbers; public: virtual void showVal(ostream &out) = 0; virtual double getAbs(void) = 0; }; const int Size = 1000; class Numbers { public: class Scanner { private: Numbers &dataBase; int pos; public: Scanner(Numbers &dataBase) : dataBase(dataBase), pos(0) { } Root *operator++(int) { if(pos == Size) return pos = 0, 0; else return dataBase.pItem[pos++]; } }; friend Root *Numbers::Scanner::operator++(int); private: Root *pItem[Size]; int count; public: Numbers(void) : count(0) { } Numbers &operator<=(Root &ref) { if(count == Size) { cout << "Numbers overflow" << endl; exit(0); } pItem[count++] = &ref; return *this; } void showAll(ostream &out) { out << "Data base contains:" << endl; for(int i = 0; i < count ; i++) { Root *ptr = pItem[i]; ptr->showVal(out); out << endl; } out << endl; } double getSum(void) { double sum; for(int i = 0; i < count ; i++) { Root *ptr = pItem[i]; sum += ptr->getAbs(); } return sum; } }; class Real : public Root { protected: double re; public: Real(double re =0) : re(re) { } double getAbs(void) { return re < 0 ? -re : re; } void showVal(ostream &out) { out << re; } }; class Cplx : public Real { protected: double re, im; public: Cplx(double re =0, double im =0) : re(re), im(im) { } double getAbs(void) { return sqrt(re * re + im * im); } void showVal(ostream &out) { out << '(' << re << ',' << im << ')'; } }; int main(void) { Real r1(1), r2(2); Cplx c1(3,4); Numbers dataBase; dataBase <= r1 <= r2 <= c1; dataBase.showAll(cout); // 1 2 (3,4) cout << "Sum = " << dataBase.getSum() << // 8 endl; cout << endl << "Scanner output:" << endl; Numbers::Scanner scan(dataBase); Root *pItem; while(pItem = scan++) { pItem->showVal(cout); cout << endl; } return 0; } Studium programowe Klas� Numbers mo�na dystrybuowa� w postaci pliku nag��wkowego numbers.h oraz pliku implementacyjnego numbers.obj. Mimo, i� kod �r�d�owy klasy jest w�wczas niedost�pny, mo�na jej u�y� w programie pos�uguj�cym si� klas� Fract, nie znan� w chwili gdy powstawa�y klasy Numbers i Scanner. Uwaga: Klasa Fract implementuje liczby u�amkowe. Ich liczniki i mianowniki s� przechowywane w postaci znormalizowanej. #include <iostream.h> #include "numbers.h" class Frac : public Root { private: int num, den; public: Frac(int num =0, int den =1) : num(num), den(den) { norm(); } int gdc(int n, int d) { int t; while(d) { t = n % d; n = d; d = t; } return n; } void norm() { int g = gdc(num, den); num /= g; den /= g; if(den < 0) { num = -num; den = -den; } } Frac operator+(Frac &par) { Frac f; f.num = num * par.den + par.num * den; f.den = den * par.den; f.norm(); return f; } operator double(void) { return double(num) / den; } double getAbs(void) { double val = double(num) / den; return val < 0 ? -val : val; } void showVal(ostream &out) { out << num << '/' << den; } }; int main(void) { Frac f1(4,8), f2(3,4); Numbers dataBase; dataBase.add(f1).add(f2); dataBase.showAll(cout); // 1/2 3/4 cout << endl; cout << "Sum = " << dataBase.getSum() << // 1.25 endl; cout << f1 + f2 << endl; // 1.25 cout << endl; cout << "Scanner output:" << endl; Numbers::Scanner scan(dataBase); Root *pItem; while(pItem = scan++) { pItem->showVal(cout); cout << endl; } return 0; } Metoda gdc wyznacza najwi�kszy-wsp�lny-podzielnik, a metoda norm normalizuje liczb� u�amkow� w taki spos�b, aby licznik i mianownik nie mia�y wsp�lnego podzielnika r�nego od 1. Studium projektowe Przedstawione tu studium projektowe ilustruje istotne problemy jakie powstaj� podczas projektowania klas. Dokonano go na przyk�adzie klasy String, kt�rej obiekty umo�liwiaj� reprezentowanie i wykonywanie operacji na �a�cuchach. Po umieszczeniu jej definicji w nag��wku string.h, klasa String mo�e by� u�yta m.in. w nast�puj�cy spos�b. #include <iostream.h> #include "string.h" int main(void) { String h("Hello"), w = "World"; cout << "The length of \"" << h + w << "\" is: " << !(h + w) << endl; return 0; } Operacja dodawania (+) s�u�y do sklejania �a�cuch�w, a operacja wykrzyknik (!) dostarcza rozmiar �a�cucha. Za�o�enia projektowe Obiekt klasy String sk�ada si� z 2 element�w: ze zmiennej typu int okre�laj�cej rozmiar �a�cucha oraz ze zmiennej typu char�* wskazuj�cej pierwszy element �a�cucha przydzielonego na stercie. Z ca�� klasa jest zwi�zana zmienna statyczna count okre�laj�ca aktualn� liczb� �a�cuch�w. class String { int len; char *ptr; static int count; }; Klasa String o podanej definicji umo�liwia tworzenie obiekt�w, inicjowanie ich za pomoc� konstruktora domy�lnego i kopiuj�cego oraz przypisywanie obiekt�w. Po uwzgl�dnieniu domniema�, tak zdefiniowana klasa jest r�wnowa�na klasie class String { private: int len; char *ptr; static int count; public: String(void) : len(), ptr() { } String(const String &par) : len(par.len), ptr(par.ptr) { } ~String(void) { } String &operator=(const String &par) { len = par.len; ptr = par.ptr; return *this; } }; Poniewa� nie zdefiniowano ani jednego jawnego konstruktora, wi�c obiekty nie s� w�a�ciwie zainicjowane i ich elementy maj� warto�ci nieokre�lone. Wobec braku operatora wyj�cia (albo konwertera), na obiektach klasy nie mo�na wykonywa� operacji wej�cia-wyj�cia. #include <iostream.h> class String { int len; char *ptr; static int count; }; int String::count = 0; int main(void) { String s1, s2(s1), s3 = s2; s1 = s3; cout << s1 << endl; // b��d cout << String::count << endl; // b��d return 0; } Wyposa�enie w konstruktor Inicjowanie element�w obiektu danymi o warto�ciach okre�lonych jest mo�liwe tylko w�wczas, gdy jego klas� wyposa�ono w konstruktor. Jego definicji mo�na nada� posta� String(char *ptr ="") : len(strlen(ptr)), ptr(new char [len+1]) { strcpy(String::ptr, ptr); count++; } Tak zdefiniowany konstruktor, poniewa� mo�e by� wywo�any bez argument�w, jest zarazem konstruktorem domy�lnym. #include <iostream.h> #include <string.h> class String { friend os