LINUXSOFT.cz Přeskoč levou lištu
Uživatel: Heslo:  
   CZUKPL

> C/C++ (39) - Objektově orientované programování

Za nejčastější důvod proč použít C++ místo C se uvádí objektově orientované programování (OOP). Dnes se zamyslíme nad tím, co to OOP vlastně je a jak se liší od klasického procedurálního programování ve stylu C.

4.10.2006 10:00 | Jan Němec | Články autora | přečteno 38255×

Objektově orientované programování

Hned ze začátku bych rád čtenáře varoval. Objektově orientovaná programování (OOP) je stále ještě určitý fenomén. O všem, co je nebo kdy bylo moderní a populární v pozitivním i negativním smyslu a do určité míry kontroverzní (nejen ve světě počítačů), si můžeme přečíst spoustu článků silně zkreslených názorem autora. Diskuse, zda je OOP dobré nebo špatné a který objektově orientovaný jazyk je nejlepší tak silně připomínají diskuse na téma Linux kontra Windows a tahanice o nejlepší linuxovou distribuci.

Programy se obvykle snaží řešit konkrétní problém a jsou určitým modelem nějakého prostředí, ať už se jedná o implementaci algoritmu z abstraktního světa matematiky, simulaci z našeho fyzického světa nebo třeba GUI knihovnu. Z vlastní programátorské praxe vím, že OOP zjednodušuje a zpřehledňuje kód (především větších projektů) všude tam, kde objekty vyskytují i v problému samotném. Pokud píšeme Eukleidův algoritmus pro hledání největšího společného dělitele dvou čísel typu int, OOP nám nepomůže, neboť v algoritmu samotném se objekty nevyskytují. Naopak třeba při diskrétní simulaci dopravního problému se nám objekty mohou hodit pro dopravní prostředky a jejich datové struktury, semafory a podobně. Celá hierarchie typů objektů pro nejrůznější prvky GUI pak bude v každé objektově orientované knihovně grafického uživatelského rozhraní. Programovat objektově orientovaný problém pochopitelně lze i klasicky, procedurálně, ve stylu C, nicméně s OOP je to jednodušší.

Občas se setkáme s názorem, že OOP pomáhá odstraňovat nepříjemné paměťové chyby. Moje programátorská praxe to nepotvrzuje, správně procedurálně navržený kód považuji za zhruba stejně bezpečný jako kvalitní kód v duchu OOP. Rozdíly vyplývají spíše z toho, že se v C obvykle programuje poněkud nižším způsobem než v C++ a jazyky se používají na řešení odlišných problémů. I zde však platí, že klasické céčkovské problémy spojené například s chybným použitím ukazatelů a přetečením zásobníku vyvažují paměťové chyby specifické pro C++ a jeho standardní knihovnu. Odhalit programátorskou chybu, která se projevuje pádem programu v kódu C++ STL knihovny bývá často těžší než detekovat chybnou alokaci v klasickém programu v C. Celkově platí, že C klade větší nároky na obecnou schopnost programovat, C++ zase na konkrétní znalosti. Ochranu proti paměťovým chybám a automatickou správu paměti poskytují až pokročilejší jazyky jako je dnes například Java nebo C#, ale ani v jejich případě to nesouvisí s OOP (případná procedurální Java by byla bezpečná stejně) a i v jejich případě to s sebou zároveň přináší i určité nevýhody.

V OOP se vyskytují pojmy třída (anglicky class) a objekt (object). Třída znamená objektový typ, objekt pak instanci daného typu. Obojí známe již z klasického procedurálního programování. Třídám se nejvíce podobají strukturované typy (typedef struct ...) a objektům odpovídají proměnné těchto typů. Pojmy typ a proměnná se používají i v OOP.

Tři základní vlastnosti OOP

OOP není jen konkrétní syntaxe jazyka, ale především způsob programování. V C lze při troše snahy psát objektově a platí to i naopak, v objektově orientovaných jazycích jako například C++ nebo Java je možné psát klasicky procedurálně. Podpora na úrovni syntaxe jazyka OOP pouze výrazně zjednodušuje. Důležité jsou 3 ideje objektově orientovaného programování: zapouzdření, dědičnost a polymorfismus. Dnes se blíže podíváme na zapouzdření, zbylé dvě vlastnosti probereme příště.

Zapouzdření (encapsulation)

Algoritmy a datové struktury řešící jeden problém patří v kódu k sobě. Na data a metody, které netvoří rozhraní by se nemělo sahat z vnějšku. Výsledkem této snahy je třída. Ta se podobá céčkovské struktuře, ale na rozdíl od ní obsahuje i funkce. Funkcím třídy se říká metody. Třída navíc může definovat oprávnění, která omezí z vnějšku přístup k datům a volání metod. Abychom si ujasnili význam zapouzdření, ukážeme si kód, který se této zásadě příčí. Budeme jej postupně upravovat, tak aby ideji zapouzdření vyhovoval.

Představme si datovou strukturu, reprezentující množinu čísel typu int, jejíchž počet předem neznáme. Datovou strukturu opakovaně používáme třeba v nějakém matematickém algoritmu, který ladíme, takže operace potřebujeme logovat. Jedním z řešení by bylo dynamicky alokované pole, které podle potřeby zvětšujeme a případně i zmenšujeme. Abychom se vyhnuli realokaci po každém přidaném číslu, můžeme alokovat například vždy na dvojnásobek předchozí hodnoty tj. pokud máme naalokováno třeba 1024 intů a chceme přidat 1025. int, provedeme realokaci rovnou na 2048. Nejhorší implementace s použitím globálních proměnných by vypadala asi takhle:

Řešení 1

int *p;
int naalokovanoPrvku;
int pocetPrvku;

void init() {
  naalokovanoPrvku = 16;
  pocetPrvku = 0;
  p = (int *) malloc(naalokovanoPrvku * sizeof(int));
}

void loguj(int i, const char *akce) {
  printf("%s %i\n", akce, i);
}

void pridej(int cislo) {
  loguj(cislo, "pridej");
  if (naalokovanoPrvku == pocetPrvku) {
    // realokace
  }
  p[pocetPrvku++] = cislo;
}

void uber(int cislo) {
  loguj(cislo, "uber");
  // ...
}

bool obsahuje(int cislo) {
  loguj(cislo, "obsahuje");
  // ...
}

void done() {
  free(p);
  p = NULL;
}

// ...

Kód jsem navrhl schválně špatně, nevyhovuje nejen OOP, ale ani zásadám procedurálního programování. Funkce využívají globální proměnné, což je vždy potenciálním zdrojem problémů. Představme si třeba, že bychom chtěli mít dvě instance datové struktury najednou. Při takto navrženém kódu bychom potřebovali nejen dvě sady proměnných, ale i dvě sady funkcí.

Řešení 2

Chceme-li strukturu používat ve více instancích a neduplikovat kód, musíme se vzdát přímého přístupu ke globálním proměnným. Funkcím tak přibudou parametry.

// proměnné pro 1. instanci
int *struktura1_p;
int struktura1_naalokovanoPrvku;
int struktura1_pocetPrvku;

// a pro druhou instanci
int *struktura2_p;
int struktura2_naalokovanoPrvku;
int struktura2_pocetPrvku;

// definice funkcí (pro všechny instance dohromady)

void init(int **p, int *pocetPrvku, int *naalokovanoPrvku) {
  *naalokovanoPrvku = 16;
  *pocetPrvku = 0;
  *p = (int *) malloc(*naalokovanoPrvku * sizeof(int));
}

void pridej(int **p, int *naalokovanoPrvku, int *pocetPrvku, int cislo) {
  // ...
}

// ostatní funkce obdobně

// ...

// použití 1. instance

init(&struktura1_p, &struktura1_naalokovanoPrvku, &struktura1_pocetPrvku);
pridej(&struktura1_p, &struktura1_naalokovanoPrvku, &struktura1_pocetPrvku, 7);
// atd.

Řešení 3

Pokud se vám druhé řešení nelíbilo, nejste sami. Neustálé předávání tří parametrů - proměnných datové struktury je velmi otravné. Pomůžeme si první vlastností OOP, zapouzdřením. Nakročením na půl cesty k OOP je již klasická céčkovská struktura, zatím tedy stále nic nového.

struct DynamickePole {
  int *p;
  int naalokovanoPrvku;
  int pocetPrvku;
};

void init(/* v C bychom sem museli napsat "struct", v C++ ne */
  DynamickePole *d) {

  d->naalokovanoPrvku = 16;
  d->pocetPrvku = 0;
  d->p = (int *) malloc(d->naalokovanoPrvku * sizeof(int));
}

void pridej(DynamickePole *d, int cislo) {
  // ...
}

// další funkce obdobně

// použití

DynamickePole d;
init(&d);
pridej(&d, 7);
// ...
Řešení 4

Je vidět, že snaha o zapouzdření má smysl i v klasickém procedurálním kódu ve stylu C, kód z třetího řešení již je docela dobře použitelný. OOP v C++ ovšem jde dál, zapouzdřuje nejen proměnné, ale i funkce. Místo klíčového slova struct se potom většinou používá nové klíčové slovo class.

class DynamickePole {
private:
  int *p;
  int naalokovanoPrvku;
  int pocetPrvku;  
public:
  void init() {
    naalokovanoPrvku = 16;
    pocetPrvku = 0;
    p = (int *) malloc(naalokovanoPrvku * sizeof(int));
  }

  void pridej(int cislo) {
    // ...
  }

  // ...

};

// Použití

DynamickePole d;
d.init();
d.pridej(7);
// ...

Uvedený kód ještě zdaleka není ideální, ale zásadu zapouzdření už splňuje i z hlediska vysokých nároků OOP. Každý objekt třídy DynamickePole obsahuje 3 proměnné, které jsou soukromé, private. To znamená, že k nim máme přístup pouze z metod třídy DynamickePole a nikoli z vnějšku. To je důležité, jde o určitou ochranu vnitřních dat objektu, tedy dat, která netvoří rozhraní třídy. Naopak všechny metody v našem případě rozhraní tvoří, jsou tedy veřejné, public a lze je volat z vnějšku. C++ samozřejmě umožňuje i naopak definovat veřejnou proměnnou a soukromou metodu. V našem případě by mohla být soukromá třeba nějaké metoda provádějící realokaci, volali bychom ji z pridej a uber. Všimněte si, že metody se píší dovnitř třídy. Nejsou to tedy globální funkce, volat je můžeme pouze přes nějakou instanci třídy DynamickePole. Možnost vlastnit kromě proměnných i metody je hlavním rozdílem C++ třídy oproti C struktuře. Do definice třídy nemusíme psát celé tělo funkce, stačí hlavička ukončená středníkem. Metodu potom definujeme podobně jako globální funkci, překladač ovšem musí nějak poznat, ke které třídě patří. Proto v tomto případě předřadíme jméno třídy a čtyřtečku.

class DynamickePole {
// ...
public:
  void init();
// ...
}

// a někde později
void DynamickePole::init() {
  // ...
}

Domácí úkol

Zkuste si některé z řešení 1 až 4 naprogramovat, samozřejmě nejlépe řešení 4. Představte si, že se jedná o knihovnu, kterou bude používat jiný programátor a rozmyslete si, která řešení pro tento případ vyhovují.

Pokračování příště

Příště se zamyslíme nad dědičností a polymorfismem.

Verze pro tisk

pridej.cz

 

DISKUZE

OOP 4.10.2006 22:02 Aleš Dostál
  |- Re: OOP 5.10.2006 08:33 Petr Zajíc
  L Re: OOP 10.1.2007 15:00 camlost




Příspívat do diskuze mohou pouze registrovaní uživatelé.
> Vyhledávání software
> Vyhledávání článků

28.11.2018 23:56 /František Kučera

Prosincový sraz spolku OpenAlt se koná ve středu 5.12.2018 od 16:00 na adrese Zikova 1903/4, Praha 6. Tentokrát navštívíme organizaci CESNET. Na programu jsou dvě přednášky: Distribuované úložiště Ceph (Michal Strnad) a Plně šifrovaný disk na moderním systému (Ondřej Caletka). Následně se přesuneme do některé z nedalekých restaurací, kde budeme pokračovat v diskusi.


Komentářů: 1

12.11.2018 21:28 /Redakce Linuxsoft.cz
22. listopadu 2018 se koná v Praze na Karlově náměstí již pátý ročník konference s tématem Datová centra pro business, která nabídne odpovědi na aktuální a často řešené otázky: Jaké jsou aktuální trendy v oblasti datových center a jak je optimálně využít pro vlastní prospěch? Jak si zajistit odpovídající služby datových center? Podle jakých kritérií vybírat dodavatele služeb? Jak volit vhodné součásti infrastruktury při budování či rozšiřování vlastního datového centra? Jak efektivně datové centrum spravovat? Jak co nejlépe eliminovat možná rizika? apod. Příznivci LinuxSoftu mohou při registraci uplatnit kód LIN350, který jim přinese zvýhodněné vstupné s 50% slevou.
Přidat komentář

6.11.2018 2:04 /František Kučera
Říjnový pražský sraz spolku OpenAlt se koná v listopadu – již tento čtvrtek – 8. 11. 2018 od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5). Tentokrát bez oficiální přednášky, ale zato s dobrým jídlem a pivem – volná diskuse na téma umění a technologie, IoT, CNC, svobodný software, hardware a další hračky.
Přidat komentář

4.10.2018 21:30 /Ondřej Čečák
LinuxDays 2018 již tento víkend, registrace je otevřená.
Přidat komentář

18.9.2018 23:30 /František Kučera
Zářijový pražský sraz spolku OpenAlt se koná již tento čtvrtek – 20. 9. 2018 od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5). Tentokrát bez oficiální přednášky, ale zato s dobrým jídlem a pivem – volná diskuse na téma IoT, CNC, svobodný software, hardware a další hračky.
Přidat komentář

9.9.2018 14:15 /Redakce Linuxsoft.cz
20.9.2018 proběhne v pražském Kongresovém centru Vavruška konference Mobilní řešení pro business. Návštěvníci si vyslechnou mimo jiné přednášky na témata: Nejdůležitější aktuální trendy v oblasti mobilních technologií, správa a zabezpečení mobilních zařízení ve firmách, jak mobilně přistupovat k informačnímu systému firmy, kdy se vyplatí používat odolná mobilní zařízení nebo jak zabezpečit mobilní komunikaci.
Přidat komentář

12.8.2018 16:58 /František Kučera
Srpnový pražský sraz spolku OpenAlt se koná ve čtvrtek – 16. 8. 2018 od 19:00 v Kavárně Ideál (Sázavská 30, Praha), kde máme rezervovaný salonek. Tentokrát jsou tématem srazu databáze prezentaci svého projektu si pro nás připravil Standa Dzik. Dále bude prostor, abychom probrali nápady na využití IoT a sítě The Things Network, případně další témata.
Přidat komentář

16.7.2018 1:05 /František Kučera
Červencový pražský sraz spolku OpenAlt se koná již tento čtvrtek – 19. 7. 2018 od 18:00 v Kavárně Ideál (Sázavská 30, Praha), kde máme rezervovaný salonek. Tentokrát bude přednáška na téma: automatizační nástroj Ansible, kterou si připravil Martin Vicián.
Přidat komentář

   Více ...   Přidat zprávičku

> Poslední diskuze

2.12.2018 23:56 / František Kučera
Sraz

5.10.2018 17:12 / Jakub Kuljovsky
Re: Jaký kurz a software by jste doporučili pro začínajcího kodéra?

20.9.2018 10:04 / Jan Ober
Jaký kurz a software by jste doporučili pro začínajcího kodéra?

20.9.2018 10:00 / Jan Ober
Re: Gimp

20.2.2018 18:48 / Ivan Majer
portal

Více ...

ISSN 1801-3805 | Provozovatel: Pavel Kysilka, IČ: 72868490 (2003-2018) | mail at linuxsoft dot cz | Design: www.megadesign.cz | Textová verze