LEKCJA 26: CO TO JEST KONSTRUKTOR. ________________________________________________________________ W trakcie tej lekcji dowiesz si�, w jaki spos�b w pami�ci komputera s� tworzone obiekty. ________________________________________________________________ C++ zawiera specjaln� kategori� funkcji - konstruktory w celu automatyzacji inicjowania struktur (i obiekt�w). Konstruktory to specjalne funkcje b�d�ce cz�onkami struktur (kategorii member functions) kt�re s� automatycznie wywo�ywane i dokonuj� zainicjowania struktury zgodnie z naszymi �yczeniami, po napotkaniu w programie pierwszej deklaracji struktury/obiektu danego typu. PRZYK�ADOWY KONSTRUKTOR. Struktura Licznik zawiera funkcj� inicjuj�c� obiekt (niech obiekt b�dzie na razie zmienn� typu struktura): struct Licznik //Typ formalny struktur { char znak; int ile; } licznik; //Przykladowa struktura void Inicjuj(char x) //Funkcja inicjuj�ca { licznik.znak = x; licznik.ile = 0; } Zdefiniujmy nasz� struktur� w spos�b bardziej "klasowo-obiektowy": struct Licznik { private: char znak; int ile; public: void Inicjuj(char); void PlusJeden(void); }; Funkcja Inicjuj() wykonuje takie dzia�anie jakie mo�e wykona� konstruktor struktury (obiektu), z t� jednak r�nic�, �e konstruktor jest wywo�ywany automatycznie. Je�li wyposa�ymy struktur� Licznik w konstruktor, to funkcja Inicjuj() oka�e si� zb�dna. Aby funkcja Inicjuj() sta�a si� konstruktorem, musimy zmieni� jej nazw� na nazw� typu struktury, do kt�rej konstruktor ma nale�e�. Zwr�� uwag�, �e konstruktor, w przeciwie�stwie do innych, "zwyk�ych" funkcji nie ma podanego typu warto�ci zwracanej: struct Licznik { private: char znak; int ile; public: Licznik(void); //Konstruktor nie pobiera argumentu void PlusJeden(void); }; Teraz powinni�my zdefiniowa� konstruktor. Zrobimy to tak, jak wcze�niej definiowali�my funkcj� Inicjuj(). Licznik::Licznik(void) //Konstruktor nie pobiera argumentu { ile = 0; } Je�li formalny typ struktur (klasa) posiada kostruktor, to po rozpocz�ciu programu i napotkaniu deklaracji struktur danego typu konstruktor zostanie wywo�any automatycznie. Dzi�ki temu nie musimy "r�cznie" inicjowa� struktur na pocz�tku programu. Jednak�e nasz przyk�adowy konstruktor nie za�atwia wszystkich problem�w - nie ustawia w strukturze zmiennej (pola) int znak - okre�laj�cego, kt�ry znak powinien by� zliczany w liczniku. W tak zainicjowanej strukturze zmienna ile jest zerowana, ale zawarto�� pola znak pozostaje przypadkowa. Niby wszystko w porz�dku, ale wygl�da to niesolidnie. Czy nie mo�naby przekaza� parametru przy pomocy konstruktora? Mo�na! Konstruktor "bezparametrowy" Licznik::Licznik(void) taki, jak powy�ej to tylko szczeg�lny przypadek - tzw. konstruktor domy�lny (ang. default constructor). PRZEKAZYWANIE ARGUMENT�W DO KOSTRUKTORA. Czasem chcemy zainicjowa� now� struktur� ju� z pewnymi ustawionymi parametrami. Te pocz�tkowe parametry struktury mo�emy przekaza� jako argumenty konstruktora. struct Licznik { private: char znak; int ile; public: Licznik(char); //Konstruktor z argumentem typu char void PlusJeden(void); }; Licznik::Licznik(char x) //Konstruktor z jednym argumentem { ... } main() { Licznik licznik('A'); //Deklaracja struktury typu Licznik // oznacza to automatyczne wywo�anie konstruktora z argumentem .... Poniew� nowy konstruktor pobiera od programu argument typu znakowego char, wi�c i definicj� konstruktora nale�y zmieni�: Licznik::Licznik(char x) //Konstruktor z jednym argumentem { ile = 0; znak = x; } Je�li parametr�w jest wi�cej ni� jeden, mo�emy je przekaza� do konstruktora, a konstruktor wykorzysta je do zainicjowania struktury w nast�puj�cy spos�b: struct Sasiedzi //s�siedzi { private: char Tab_imion[4]; ... public: Sasiedzi(char *s1, char *s2, char *s3, char s4); ... }; main() { Sasiedzi chopy("Helmut", "Ulrich", "Adolf", "Walter"); .... Przekazanie konstruktorowi argument�w i w efekcie automatyczne ustawiamie przez konstruktor paramatr�w struktury ju� w momencie zadeklarowania struktury w programie rozwi�zuje wiele problem�w. W C++ istnieje jednak�e pewne do�� istotne ograniczenie - nie mo�emy zadeklarowa� tablicy z�o�onej z obiekt�w posiadaj�cych konstruktory, chyba �e wszystkie konstruktory s� bezparametrowe (typu default constructors). Udoskonalmy teraz nasz program zliczaj�cy wyst�pienia w tek�cie litery a pos�uguj�c si� konstruktorem struktury. [P094.CPP] /* Wersja ze struktur� */ # include <ctype.h> # include <iostream.h> struct Licznik { private: char znak; int ile; public: Licznik(char); //Konstruktor void PlusJeden(void); char Pokaz(void); int Efekt(void); }; Licznik::Licznik(char x) //Def. konstruktora { znak = x; ile = 0; } void main() { Licznik licznik('A'); //Zainicjowanie przez konstruktor cout << "Sprawdzamy: znak ile? " << "\n\t\t" << licznik.Pokaz() << "\t"; cout << licznik.Efekt(); cout << "\nWpisz tekst zawierajacy litery A"; cout << "\nPierwsze wytapienie litery k lub K"; cout << "\n - oznacza Koniec zliczania: "; for(;;) { char znak_we; cin >> znak_we; if (znak_we == 'k' || znak_we == 'K') break; if(licznik.Pokaz() == toupper(znak_we)) licznik.PlusJeden(); } cout << "\nLitera " << licznik.Pokaz() << " wystapila " << licznik.Efekt() << " razy."; } /* Definicje pozosta�ych funkcji: */ void Licznik::PlusJeden(void) { ile++; } char Licznik::Pokaz(void) { return (znak); } int Licznik::Efekt(void) { return (ile); } Po zamianie s�owa kluczowego struct na class (licznik ze struktury stanie si� obiektem, a Licznik - z formalnego typu struktur - klas�) wystarczy w programie zlikwidowa� zb�dne s�owo "private" i wersja obiektowa programu jest gotowa do pracy. [P095.CPP] /* Wersja z klas� i obiektem */ # include <ctype.h> # include <iostream.h> class Licznik { char znak; int ile; public: Licznik(char); //Konstruktor void PlusJeden(void); char Pokaz(void); int Efekt(void); }; Licznik::Licznik(char x) //Def. konstruktora { znak = x; ile = 0; } void main() { Licznik licznik('A'); //Zainicjowanie obiektu licznik cout << "Sprawdzamy: znak ile? " << "\n\t\t" << licznik.Pokaz() << "\t"; cout << licznik.Efekt(); cout << "\nWpisz tekst zawierajacy litery A"; cout << "\nPierwsze wytapienie litery k lub K"; cout << "\n - oznacza Koniec zliczania: "; for(;;) { char znak_we; cin >> znak_we; if (znak_we == 'k' || znak_we == 'K') break; if(licznik.Pokaz() == toupper(znak_we)) licznik.PlusJeden(); } cout << "\nLitera " << licznik.Pokaz() << " wystapila " << licznik.Efekt() << " razy."; } void Licznik::PlusJeden(void) { ile++; } char Licznik::Pokaz(void) { return znak; } int Licznik::Efekt(void) { return ile; } Pora w tym miejscu zaznaczy�, �e C++ oferuje nam jeszcze jedno specjalne narz�dzie podobnej kategorii. Podobnie, jak do tworzenia (struktur) obiekt�w mo�emy zastosowa� konstruktor, tak do skasowania obiektu mo�emy zastosowa� tzw. desruktor (ang. destructor). Nazwy konstruktora i destruktora s� identyczne z nazw� macie�ystego typu struktur (macie�ystej klasy), z tym, �e nazwa destruktora poprzedzona jest znakiem "~" (tylda). CO TO JEST DESTRUKTOR. Specjalna funkcja - destruktor (je�li zadeklarujemy zastosowanie takiej funkcji) jest wywo�ywana automatycznie, gdy program zako�czy korzystanie z obiektu. Konstruktor towrzy, a destruktor (jak sama nazwa wskazuje) niszczy struktur� (obiekt) i zwalnia przyporz�dkowan� pami��. Przyk�ad poni�ej to program manipuluj�cy stosem, rozbudowany tak, by zawiera� i konstruktor i destruktor struktury (obiektu). Zorganizujmy zarz�dzanie pami�ci� przeznaczon� dla stosu w taki spos�b: struct Stos { private: int *bufor_danych; int licznik; public: Stos(int ile_RAM); /* Konstruktor int Pop(int *ze_stosu); int Push(int na_stos); }; gdzie: *bufor_danych - wska�nik do bufora (wype�niaj�cego rol� stosu), licznik - wierzcho�ek stosu, je�li == -1, stos jest pusty. Stos::Stos(...) - konstruktor inicjuj�cy struktur� typu Stos (lub obiekt klasy Stos), ile_RAM - ilo�� pami�ci potrzebna do poprawnego dzia�anie stosu, *ze_stosu - wska�nik do zmiennej, kt�rej nale�y przypisa� warto�� zdj�t� w�a�nie ze stosu, na_stos - liczba przeznaczona do zapisu na stos. Zajmijmy si� teraz definicj� konstruktora. Wywo�uj�c konstruktor w programie (deklaruj�c u�ycie w programie struktury typu Stos) przeka�emy mu jako argument ilo�� potrzebnej nam pami�ci RAM w bajtach. Do przy...
mikomil