Programowanie zorientowane obiektowo część 1


[Komentarz LeMUra]
[Komentarz Kaczusia]


Od LeMUra:
Pozwoliłem sobie na pewne wstawki w artykule, które mają na celu pokazanie co i jak w zwykłym C. Mam nadzieję, że to komuś pomoże...


Jak bardzo się zestarzał ten artykuł bedzie można zobaczyć czytając książkę opisującą standard C++11, no ale minęło od tamtego czasu kilkanaście lat

Zanim napiszę co to takiego jest programowanie zorientowane obiektowo, napiszę może najpierw o kompilatorach i językach programowania. Co do języka programowania, to na pewno C++, dla piszących strony WWW - skrypty Jawy, i podobno język E (nie jest to informacja potwierdzona). Co do kompilatorów to niestety na Amidze jest mały problem. Kompilatory, które miałem okazję przetestować nie zawsze pracowały bezbłędnie [obecnie polecam kompilatory na licencji GNU], lecz mam nadzieję, że w najbliższym czasie (w związku z powstaniem systemu POS) ukażą się poprawione wersje kompilatorów. Dla pocieszenia mogę podać,iż kompilator Microsoftu - VisualC++, również posiada błędy - nie wspominając o obsłudze tego kompilatora - z wizualnym programowaniem, po zmianie nazwy miał by niewiele wspólnego. [z doświadczeń z versja 1.52 i 2.0 tego kompilatora] Na razie ze znanych mi kompilatorów C++, mogę polecić: SasC, StormC, MaxonC++ [Wersja anglojęzyczna - HisoftC], GnuC. Być może jest ich jeszcze kilka, lecz na razie nie wpadły w moje ręce.


Teraz może mały wstępniak dotyczący programowania zorientowanego obiektowo. Podstawowym pojęciem w tym sposobie programowania, jak sama nazwa wskazuje, jest obiekt.


Cóż to jest ten obiekt?


Otóż jest to pewien twór, który ma być odzwierciedleniem pewnej rzeczywistości -prawdopodobnie brzmi to trochę mętnie, dlatego może podam przykład. Powiedzmy że mamy deskorolkę, teraz zastanówmy się z czego ona się składa, oraz jakie są jej zadania? Składniki deskorolki to deska i cztery kółka; natomiast zadania to jazda prosto i skręty. W podobny sposób można opisać taką deskorolkę w C++:



class Deskorolka
{
   public:        // - na razie nie zważaj na tą etykietę, o tym później
   Deska deska;
   Kolo kolko1;   // - ewentualnie możemy zastąpić czteroelementową tablicą 
   Kolo kołko2;   // Kolo Kolka[4];
   Kolo kolko3;
   Kolo kołko4;
   Jedz();
   Skrecaj();
};

Jak widać na powyższym przykładzie obiekt (tu class) deskorolka, różni się tym od struktury (znanej z klasycznego C), iż po za polami opisującymi dany obiekt, zdefiniowane są również funkcje, opisujące funkcje danego obiektu.

/* LeMUr komentuje */ W zwykłym C też można przecież zdefiniować funkcje opisujące dany obiekt! Powyższy przykład wyglądałby (najprawdopodobniej -nie jestem tego pewien!) tak:


struct Deskorolka
{
   Deska deska;   /* zakładam, że "Deska" to jakiś typ danych? */ 
   Kolo kolko1;   /* - ewentualnie możemy zastąpić czteroelementową tablicą */
   Kolo kołko2;   /* Kolo Kolka[4]; przy czym "Kolo" to też typ danych */  
   Kolo kolko3;
   Kolo kołko4;
   (void *) Jedz;      /* funkcja Jedz() musi być zdefiniowana wcześniej */ 
   (void *) Skrecaj;   /* j/w - jest to wskaźnik do funkcji */ 
};
/* koniec komentarza */

Zarówno funkcje, jak i pola obiektu nazywać będziemy składowymi. Możliwość definiowania funkcji, w ciele obiektu, to nie jedyna zaleta, lecz o tym dowiedzą się wytrwali, którzy dotrwają do końca kursu (jeżeli oczywiście kurs będzie ukazywał się dalej). Ale powróćmy do tematu. Mamy więc obiekt, w którym zadeklarowaliśmy funkcje. Zdefiniować je można w ciele klasy (jeżeli definicja funkcji jest dłuższa niż dwie, trzy linijki, to nie polecam tego rozwiązania - jest mało eleganckie), lub też poza klasą.

Przykład definicji w ciele klasy:

class Deskorolka
{
   public:
   ...
   Jedz()
   {
      ...
   }
};
Przykład definicji po za klasą:

class Deskorolka
{
   public:
   ...
   Jedz();
};
Deskorolka::Jedz()
{
   ...
}

/* LeMUr: */
Tutaj widać różnicę pomiędzy C a C++ - w tym pierwszym można definiować tylko "poza klasą" :)

W przykładzie drugim pojawił się przy definicji klasy poza nazwą i definicją funkcji też operator, który informuje kompilator do jakiej klasy (jakiego obiektu) definiowana funkcja należy. Dzięki temu możemy definiować dla różnych obiektów funkcje o tych samych nazwach. [kolejna różnica - w C muszą to być różne nazwy - LeMUr]. Na przykład możemy dla obiektu rower również możemy zdefiniować funkcję Jedz(). Oczywiście nie ma róży bez kolców. W zamian za te dobrodziejstwa musimy pamiętać, iż funkcje składowe możemy wywoływać jedynie na rzecz obiektów. Oto sposoby wywołań tych funkcji:



   Deskorolka obiekt; // deklaracja (jak się przekonasz dalej - wraz z
                      // definicją) obiektu obiekt 
   Deskorolka *Wskaznik; // deklaracja (powinna znależć się również definicja,
                         // lecz o tym pare linijek niżej gdy zdefiniujemy
                         // sobie co to jest konstruktor) wskażnika na obiekt; 
   obiekt.Jedz(); // pierwszy sposób: wywołanie funkcji na rzecz obiektu o
                  // nazwie obiekt 
   Wskaznik->Jedz(); // drugi sposób: wywołanie funkcji Jedz na rzecz obiektu
                     // wskazywanego przez wskaznik Wskaznik  
   (*Wskaznik).Jedz(); // trzeci sposób: dokładnie jak drugi sposób 
   Deskorolka::Jedz(); // czwarty sposób: ponieważ jest to bardziej
                       // skomplikowane, to wyjaśnię ten sposób wraz z
                       // omawianiem składowych statycznych. 

/* LeMUr: */
W C wywołać funkcję, do której mamy wskaźnik można na przykład tak:

      int (*func)(void);
      int wynik = FALSE;
      func = (int (*)(parametry))wskaźnik_do_funkcji;
      wynik = func();
W wypadku wcześniej zdefiniowanej struktury wyglądałoby np. to:

      func = (int (*)())Deskorolka.Jedz;
      wynik = func();

Konstruktor i destruktor.


Proszę się nie bać mimo tak strasznie brzmiących nazw nie jest to nic trudnego, aczkolwiek dosyć ważny temat z punktu widzenia programowania zorientowanego obiektowo. Cóż to takiego te konstruktory i destruktory? Otóż są to funkcje składowe, że tak powiem "specjalnego znaczenia". Konstruktor to funkcja o tej samej nazwie co klasa, przed którą nie stawiamy żadnego operatora typu (nawet void, mimo iż funkcja ta nie zwraca żadnej wartości). Dowolna klasa może mieć więcej niż jeden konstruktor, pod warunkiem, że funkcje te różnią się przynajmniej jednym argumentem (czyli można je przeciążać). UWAGA: Jeżeli istnieje konstruktor i nie jest on bezargumentowy to przy deklaracji obiektu musimy wywołać ten konstruktor w przeciwnym razie kompilator zgłosi błąd.

Przykłady wywołań konstruktora:

class Deskorolka
{
   public: 
   Deska deska;
   Kolo kolko1;	 
   Kolo kołko2; 
   Kolo kolko3;
   Kolo kołko4;
   Deskorolka(kolor);   // konstruktor, którego argumentem jest kolor
   Jedz();
   Skrecaj();
};
   Deskorolka obiekt(Czerwona);
   Deskorolka *Wskaznik = new Deskorolka(Czerwona);
   Deskorolka::Deskorolka(Czerwona);

Można użyć jeszcze kilku innych sposobów wywołania konstruktora, lecz w wyniku użycia tamtych sposobów nie uzyskamy nic innego niż w podanych tu przykładach. W wyniku użycia pierwszej deklaracji utworzony zostanie obiekt z argumentem Czerwona. W wyniku użycia drugiej z deklaracji utworzony zostanie obiekt, którego adres zostanie przypisany wskaźnikowi Wskaznik. Uwaga, gdy tak zdefiniowany obiekt nie będzie nam już potrzebny powinniśmy napisać instrukcję:



   delete Wskaznik;

w przeciwnym razie obszar pamięci zajmowany przez ten obiekt nie zostanie zwolniony). Trzeci przypadek zostawimy sobie na później.


Teraz może parę słów na temat do czego nam potrzebny jest konstruktor. Otóż konstruktor to taki "dekorator wnętrz", tzn w konstruktorze definiujemy na przykład jaki kolor ma mieć deska w deskorolce.


Destruktor.


Destruktor jest to bezargumentowa funkcja, nie zwracająca (podobnie jak konstruktor) żadnej wartości. Charakteryzuje się tym, że nazwa tej funkcji to nic innego, jak nazwa obiektu poprzedzona znakiem tyldy (~). Na przykład:



class Deskorolka
{
   public: 
   Deska deska;
   Kolo kolko1;	 
   Kolo kołko2; 
   Kolo kolko3;
   Kolo kołko4;
   Deskorolka(kolor);
   ~Deskorolka();
   Jedz();
   Skrecaj();
};

Destruktor wywoływany jest automatycznie dla obiektów definiowanych w pierwszy sposób, natomiast w pozostałych przykładach należy wywołać destruktor odpowiednio: dla drugiego przykładu:


   delete Wskaznik;
   Deskorolka::~Deskorolka();

Po co nam destruktor? - do sprzątania, czyli poinformowania systemu, że może już zwolnić pamięć zajmowaną przez dany obiekt. Uwaga: destruktora nie można przeciążać


Na tym kończę pierwszą część wykładu na temat programowania zorientowanego obiektowo. Do artykułu dołączam krótki przykład wykorzystujący elementy opisane w powyższym artykule. W razie problemów polecam książkę Jerzego Grębosza "Symfonia C++, programowanie orientowane obiektowo" (jest to książka pisana bardzo prostym językiem), lub też o kontakt ze mną: kaczus@poczta.onet.pl

Dziękuję za uwagę

Kaczuś

.
Powrót do Menu