Programowanie obiektowe

Programowanie obiektowe stanowi zupełnie nową jakość programowania. Dzięki niemu wiele spraw trudnych do realizacji staje się znacznie łatwiejszych. Zagadnienie zostaje rozbite na mniejsze elementy, które oddają rzeczywistość, a dane są reprezentowane w sposób naturalny. Programista może się znacznie mocniej skupić na tym, co chce osiągnąć, zamiast zwracać uwagę na realizację techniczną. Kod staje się przejrzysty i łatwy w interpretacji. Jedną z cech programowania obiektowego jest zapewnienie hermetyczności kodu, dzięki czemu jest on uodporniony na błędy. Znacznie łatwiej prowadzi się też projekty grupowe, ponieważ implementacja poszczególnych elementów nie wpływa na działanie całości kodu. Łatwiej również przekazać pracę innemu programiście, ponieważ nowa osoba w czasie poznawania programu może przechodzić od zagadnień najbardziej ogólnych do szczegółowych.

Więcej informacji: https://pl.wikipedia.org/wiki/Programowanie_obiektowe

Struktura

Struktura to typ danych pozwalający gromadzić szczegółowe dane w większym obiekcie. Strukturę deklaruje się w następujący sposób:

struct nazwa_struktury
{
  pola struktury
};

Przykładowa struktura:

// punkt na płaszczyźnie
struct punkt
{
  double x,y; // współrzędne punktu
};

Sposób użycia:

// deklaracja zmiennej typu punkt
punkt p;

// sposób dostępu do zmiennej
p.x = 10.0;
p.y = 20.0;

Klasa

Klasa to typ danych, który podobnie jak struktura pozwala gromadzić dane, ale dodatkowo posiada własny zestaw funkcji (tzw. metod). Klasę deklaruje się w następujący sposób:

class nazwa_klasy
{
  pola klasy

  metody klasy
};

Przykładowa klasa:

// obiekt wielobok
class wielobok
{
  private:
    double szerokość,
           wysokosc;

  public:
    void ustaw_wymiary(double a, double b);
};

void wielobok::ustaw_wymiary(double a, double b)
{
  szeroskosc = a;
  wysokość = b;
}

W przykładzie zdefiniowano obiekt wielobok. Ma on własne pola (wymiary), które są niedostępne „z zewnątrz”, tzn. ich wartości mogą być modyfikowane tylko przez metody obiektu. Dzięki temu pola te są zabezpieczone przez wpisaniem wartości w niewłaściwy sposób. Oczywiście, nic nie stoi na przeszkodzie, aby te pola były dostępne – wystarczy zmienić słowo pivate na public.

Obiekt posiada jedną metodę ‘ustaw_wymiary’, która nadaje wartości zmiennym ‘szerokosc’ i ‘wysokość’. Metoda jest zadeklarowana w obiekcie, a zdefiniowana poza nim.

Przykładowe użycie obiektu:

// deklaracja zmiennej wielobok
wielobok wb;

// wywołanie metody
wb.ustaw_wymiary(3.0, 2.5);

Dziedziczenie

Jedną z zalet programowania obiektowego, jest możliwość dziedziczenia. Ten mechanizm umożliwia przekazywanie właściwości z jednej klasy do innej. Przez właściwości rozumie się zarówno pola, jak i metody. Klasę, z której się dziedziczy nazywa się rodzicem, a klasę która dziedziczy – potomkiem lub klasą potomną.

Przykład 1.

// obiekt prostokąt
class prostokat: public wielobok
{
  public:
    double pole() { return szerokosc*wysokosc;  }
};

Zdefiniowano klasę prostokąt, która w dziedziczy z klasy wielobok. Obiekt prostokąt ma swoje wymiary i metodę ‘ustaw_wymiary’ (odziedziczone z wieloboku). Ma też swoją własną metodę obliczającą pole powierzchni. Metody można definiować wewnątrz obiektu, tak jak to pokazuje przykład. W przypadku krótkich funkcji nie pogarsza to czytelności kodu. Zasadniczo wewnątrz obiektu powinno się deklarować funkcje, a definiować je na zewnątrz. Łatwiej znaleźć dostępne funkcje w obiekcie, w którym są krótkie zapowiedzi.

Przykładowe użycie obiektu:

// deklaracja prostokąta
prostokat pr;

// ustawienie wymiarów
pr.ustaw_wymiary(3.0, 2.5);

// obliczenia pola powierzchni
double x = pr.pole();

Przykład 2.

// obiekt trójkąt
class trojkat: public wielobok
{
  public:
    double pole() { return (szerokosc*wysokość)/2.0;  }
};

Przykładowe użycie obiektu:

// deklaracja trójkąta
trojkat tr;

// ustawienie wymiarów
tr.ustaw_wymiary(3.0, 2.5);

// obliczenia pola powierzchni
double x = tr.pole();

Jak widać, obiekty programu w intuicyjny sposób oddają relacje między pojęciami geometrycznymi. Definicja wymiarów prostokąta i trójkąta jest jedna, bo nie ma potrzeby jej powielania. A funkcje obliczające pole są indywidualne, ale korzysta się z nich w jednakowy sposób. Można oczywiście napisać program w takim stylu:

// deklaracja trójkąta
double tr_szerokosc,
       tr_wysokosc;

// ustawienie wymiarów
tr_szerokosc = 3.0;
tr_wysokosc = 2.5;

// obliczenia pola powierzchni
double x = tr_szerokosc*tr_wysokosc;

Jednak jest to efektywne tylko przy małych zagadnieniach. Przy dużych projektach, z dużą liczbą zmiennych i skomplikowanymi zależnościami między nimi, zalety programowania obiektowego stają się widoczne.

Zaprzyjaźnianie, przeciążanie

Do tego momentu, w przykładach prezentowane były metody (funkcje działające na samym obiekcie). Ich wywołanie następowało z wykorzystaniem znaku ‘.’ (np. tr.pole() ). Aby napisać funkcję, która działa na argumencie typu trójkąt, trzeba skorzystać z mechanizmu zaprzyjaźniania funkcji. Robi się to tak:

// obiekt trójkąt
class trojkat: public wielobok
{
  public:

    friend double pole(const trojkat &t) { return (t.szerokosc*t.wysokość)/2.0;  }
};

Zadeklarowano funkcję „przyjaciela” (friend). Argumentem tej funkcji jest trójkąt ‘t’. Ten argument zostaje przekazany do funkcji ‘pole’ poprzez referencję (&). Dzięki temu program będzie działał szybciej (obiekt nie będzie kopiowany pole po polu). Słowo const sygnalizuje, że obiekt ‘t’ nie zostanie zmieniony wewnątrz funkcji ‘pole’. Funkcja korzysta tylko z jego wymiarów. Jak widać odbywa się to tak: t.szerokosc, t.wysokosc. Gdyby funkcja ‘pole’ nie była zaprzyjaźniona, ten sposób odwołania do wymiarów ‘t’ byłby zabroniony, ponieważ pola ‘szerokosc’ i ‘wysokosc’ są prywatne. Zaprzyjaźnienie funkcji umożliwiło swobodny dostęp do prywatnych pół obiektu.

Wywołanie funkcji zaprzyjaźnionej odbywa się całkiem normalnie:

double x = pole(tr);

W C++ możliwe jest przeciążanie funkcji. Można napisać wiele funkcji o tej samej nazwie, różniących się argumentami. Na przykład:

// obiekt trójkąt
class trojkat: public wielobok
{
  public:

    friend double pole(const trojkat &t) { return (t.szerokosc*t.wysokość)/2.0;  }
    double pole() { return (szerokosc*wysokość)/2.0;  }
};

Teraz można obliczyć pole na dwa sposoby:

double x1 = pole(tr);

double x2 = tr.pole();

W zależności od sposobu przekazania argumentów, wywoływane są różne funkcje. Każda z funkcji przeciążonych może robić inne obliczenia (np. jedna w metrach, a druga w calach).

Zaprzyjaźniać można też całe klasy, nie tylko pojedyncze funkcje. Wówczas jedna klasa może swobodnie korzystać z pól i metod drugiej klasy.

Więcej informacji o technikach programowania można znaleźć w sieci.

Polecany tutorial: http://www.cplusplus.com/doc/tutorial/