9. Zßklady OOP IX
  1. Nßsledujφcφ funkce provßdφ °azenφ n prvk∙ pole typu int podle velikosti.

  2. void Razeni(int *a, int n){
      for (int w = 0; w < n; ++w){
        int k = w;
        for (int l = w+1; l < n; l++) if (a[l] < a[k]) k = l;
        if (k != w) {
          int s = a[w];
          a[w] = a[k];
          a[k] = s;
        }
      }
    }
    ╪azenφ prvk∙ pole se vyskytuje Φasto a bylo by tedy vhodnΘ tuto funkci vlo╛it do knihovny. ProblΘm je, ╛e ne v╛dy pot°ebujeme se°adit prvky typu int. M∙╛eme vyjφt z uvedenΘho °e╣enφ a kdy╛ budeme pot°ebovat °adit pole s prvky jinΘho typu, staΦφ nahradit v╣echna klφΦovß slova int oznaΦenφm novΘho typu. To ale nenφ nejlep╣φ nßpad; co kdy╛ n∞kterΘ int zapomeneme zm∞nit nebo zm∞nφme i int, kterΘ se typu prvk∙ °azenΘho pole net²kß. Nabφzejφ se nßm ale dv∞ rozumn∞j╣φ °e╣enφ.
    V╣echny v²skyty klφΦovΘho slova int, kterΘ oznaΦujφ typ °azenΘho pole, nahradφme n∞jak²m obecn²m identifikßtorem, nap°. TYP a tomuto identifikßtoru dßme v²znam pomocφ typedef. Nap°.
    typedef int TYP;
    Vyzkou╣ejte zm∞nit na╣i funkci v tomto smyslu.
  3. DruhΘ °e╣enφ je deklarovat celou funkci jako makro, jeho╛ parametrem bude typ °azenΘho pole:

  4. #define RAZENI(TYP) void Razeni(TYP *a, int n){              \
      for (int w = 0; w < n; ++w){                               \
        int k = w;                                               \
        for (int l = w+1; l < n; l++) if (a[l] < a[k]) k = l;    \
        if (k != w) {                                            \
          TYP s = a[w];                                          \
          a[w] = a[k];                                           \
          a[k] = s;                                                \
        }                                                        \
      }                                                          \
    }
    Vyzkou╣ejte v n∞jakΘm programu.
  5. Jazyk C++ nabφzφ lep╣φ °e╣enφ p°edchozφho problΘmu a to pou╛itφ ╣ablony. Naprogramujeme funkci Razeni jen jednou a to jako ╣ablonu. Typ prvk∙ pole bude parametrem tΘto ╣ablony. P°ekladaΦ pak v p°φpad∞ pot°eby vytvo°φ podle tΘto ╣ablony funkci s po╛adovan²mi vlastnostmi (podle typu urΦenΘho parametrem ╣ablony). ⌐ablony se v mnohΘm podobajφ makr∙m, zpracovßvß je ale p°ekladaΦ a ne preprocesor. Deklarace ╣ablony v programu p°edstavuje abstraktnφ vzor, podle kterΘho je p°ekladaΦ schopen definovat celΘ skupiny funkcφ. Funkce vytvo°enΘ podle ╣ablony naz²vßme instance ╣ablony. V C++ m∙╛eme deklarovat ╣ablony normßlnφch funkcφ, ╣ablony t°φd a jejich metod.

  6. Deklaraci ╣ablony m∙╛eme v programu C++ zapsat pouze na ·rovni souboru a mß tento obecn² tvar:
    template<seznam_par>deklarace_╣ablony
    Za klφΦov²m slovem template nßsleduje v lomen²ch zßvorkßch seznam formßlnφch parametr∙ ╣ablony. Jsou to formßlnφ parametry odd∞lenΘ Φßrkami. ⌐ablony mohou mφt typovΘ a hodnotovΘ parametry. HodnotovΘ parametry jsou parametry, s jak²mi se setkßvßme u obyΦejn²ch funkcφ. Mohou b²t skalßrnφch typ∙. Nelze pou╛φt objektovΘ typy nebo pole. Nelze zde takΘ pou╛φt v²pustku. U hodnotov²ch parametr∙ m∙╛eme p°edepsat implicitnφ hodnoty. TypovΘ parametry jsou uvedeny klφΦov²m slovem class (u nov∞j╣φch p°ekladaΦ∙ je zde klφΦovΘ slovo typename) a specifikujφ datovΘ typy. SkuteΦn²m parametrem ╣ablony, kter² odpovφdß formßlnφmu parametru, m∙╛e b²t oznaΦenφ libovolnΘho datovΘho typu. Deklarace_╣ablony znamenß deklaraci normßlnφ funkce, objektovΘho typu nebo metody podle obvykl²ch pravidel. V tΘto deklaraci mohou b²t n∞kterΘ konstanty nahrazeny hodnotov²mi parametry a n∞kterß oznaΦenφ typ∙ typov²mi parametry.
    Nejd°φve se budeme zab²vat ╣ablonami °adov²ch funkcφ. ⌐ablona °adovΘ funkce musφ mφt pouze typovΘ parametry. Jako p°φklad si uvedeme deklaraci ╣ablony funkce Razeni:
    template<class TYP> void Razeni(TYP *a, int n){
      for (int w = 0; w < n; ++w){
        int k = w;
        for (int l = w+1; l < n; l++) if (a[l] < a[k]) k = l;
        if (k != w) {
          TYP s = a[w];
          a[w] = a[k];
          a[k] = s;
        }
      }
    }
    ⌐ablona Razeni mß jeden formßlnφ parametr TYP, kter² p°edstavuje datov² typ. SkuteΦn²m parametrem tΘto ╣ablony m∙╛e b²t libovoln² datov² typ, pro kter² jsou definovßny v╣echny operßtory pou╛itΘ pro tento typ v ╣ablon∞. Instance ╣ablony normßlnφ funkce budou funkce se stejn²m identifikßtorem, kterΘ se budou li╣it typem parametr∙. M∙╛eme je generovat bu∩ explicitn∞ nebo implicitn∞. ZaΦneme u implicitnφho generovßnφ instancφ. P°edpoklßdejme, ╛e jsme v programu deklarovali v²╣e uvedenou ╣ablonu Razeni. Je-li a pole typu int, pak p°φkaz
    Razeni(a, 10);
    zp∙sobφ, ╛e p°ekladaΦ automaticky vytvo°φ instanci void Razeni(int *, int). Podobn∞ deklarujeme-li v programu ╣ablonu
    template<class T> T max(T a, T b){
      return a < b ? b : a;
    }
    p°φpadn∞ pro nov∞j╣φ p°ekladaΦe
    template<typename T> T max(T a, T b){
      return a < b ? b : a;
    }
    a prom∞nnΘ x a y typu int, zp∙sobφ zßpis
    int k = max(x, y);
    ╛e se prom∞nnΘ k p°i°adφ hodnota v∞t╣φho z Φφsel ulo╛en²ch v x a y. Bude-li ale z typu char, pak zßpis
    z = max(x, z);
    je chybn², nebo╗ takovou funkci podle na╣φ ╣ablony vytvo°it nelze. ⌐ablona max toti╛ p°edpoklßdß, ╛e typy obou parametr∙ jsou shodnΘ. ⌐ablona nenahrazuje prototyp funkce, tak╛e nelze spolΘhat na konverzi parametr∙. To znamenß, ╛e pokud bychom deklarovali v programu konstantu
    const int N = 1000;
    a pokusili se vytvo°it instanci ╣ablony Razeni zßpisem
    Razeni(a, N);
    protestoval by p°ekladaΦ, ╛e neumφ najφt funkci Razeni(int *, const int) a my bychom museli pou╛φt p°etypovßnφ
    Razeni(a, (int)N);
    nebo explicitnφ vytvo°enφ instance jak je uvedeno dßle. Pokuste se vyzkou╣et ╣ablonu funkce Razeni v n∞jakΘm programu.
  7. Explicitnφ generovßnφ funkcφ se pon∞kud li╣φ ve star╣φch a nov∞j╣φch p°ekladaΦφch C++. V nov²ch p°ekladaΦφch (odpovφdajφcφch ANSI C++ - nap°. Borland C++ 5.0) se pro explicitnφ generovßnφ instancφ pou╛φvß syntaxe

  8. template prototyp;
    To znamenß, ╛e instanci ╣ablony Razeni vytvo°φme zßpisem
    template void Razeni(int *, int);
    a instanci ╣ablony max zßpisem
    template double max(double, double);
    Tyto deklarace nahrazujφ prototyp, tak╛e p°ekladaΦ m∙╛e provßd∞t konverze parametr∙.
    N∞kdy se dostaneme do situace, kdy pro urΦit² typ parametr∙ nßm instance vytvo°enß podle ╣ablony nevyhovuje. Nic nßm nap°. nebrßnφ generovat instanci char * max(char *, char *), kterß bude porovnßvat dva znakovΘ °et∞zce podle adresy. Pro nßs mß ale podstatn∞ v∞t╣φ v²znam porovnßvßnφ znakov²ch °et∞zc∙ podle abecedy. V C++ nßm na╣t∞stφ deklarace ╣ablony nebrßnφ, abychom definovali funkci se stejn²m jmΘnem a s pot°ebn²m typem parametr∙:
    char * max(char *a, char *b){
      ...// porovnßnφ °et∞zc∙
    }
    Tato deklarace zastφnφ ╣ablonu (p°esn∞ji °eΦeno poklßdß ji za instanci ╣ablony) a nemusφme se obßvat, ╛e by p°ekladaΦ podle ╣ablony vytvo°il nesmyslnou funkci.
  9. Deklarace ╣ablony t°φdy nebo metody mß op∞t tvar

  10. template<sez_par>deklarace;
    St°ednφk na konci je podle normy nezbytn². ⌐ablony t°φd a jejich metod mohou mφt (na rozdφl od ╣ablon normßlnφch funkcφ) nejen typovΘ, ale i hodnotovΘ parametry. SkuteΦnΘ hodnotovΘ parametry p°i pou╛itφ ╣ablony musφ b²t konstantnφ v²razy, tj. musφ je um∞t vyhodnotit ji╛ p°ekladaΦ. V tΘto deklaraci m∙╛eme k oznaΦenφ typu pou╛φt typovΘ formßlnφ parametry a jako konstanty pou╛φt hodnotovΘ formßlnφ parametry. V deklaraci ╣ablony t°φdy m∙╛eme zapsat i vlo╛enΘ metody a vlo╛enΘ sp°ßtelenΘ funkce. Ostatnφ metody musφ mφt svΘ vlastnφ ╣ablony. V nßsledujφcφm p°φkladu deklarujeme ╣ablonu t°φdy pro prßci s dynamicky alokovan²m jednorozm∞rn²m polem prom∞nnΘ dΘlky. Jako parametr ╣ablony zadßme poΦet prvk∙ pole a jejich typ.
    template<class T, int N =2> class Pole {
      int delka;
      T* p;
    public:
      Pole(T r=0);
      Pole(Pole&);
      ~Pole() {delete p;};
      Pole& operator= (Pole&);
      T& operator[] (int i) {return p[i];}
      Pole operator*(double);
      friend Pole<T,N> operator* (double d, Pole<T,N>& Q) {return Q*d;}
    };
    Tato ╣ablona mß typov² parametr T a hodnotov² parametr N (mß implicitnφ hodnotu). Identifikßtor Pole je jmΘno ╣ablony. V deklaraci ╣ablony t°φdy je lze pou╛φvat bez parametr∙, p°i ostatnφch pou╛itφch v╣ak musφ b²t spojeno se skuteΦn²mi parametry. Pro ╣ablonu t°φdy, kterß obsahuje ukazatel na dynamicky alokovanΘ pole, jsme museli deklarovat kopφrovacφ konstruktor, destruktor a p°i°azovacφ operßtor. Snadn² p°φstup ke slo╛kßm tohoto pole nßm umo╛nφ operßtor indexovßnφ. Dßle jsme zde deklarovali operßtor *, kter² vynßsobφ v╣echny prvky pole dan²m Φφslem. Operßtor nßsobenφ Φφslem zprava deklarujeme jako metodu (prvnφ operand je Pole), operßtor nßsobenφ Φφslem zleva (v po°adφ Φφslo*Pole) deklarujeme jako sp°ßtelenou funkci. Operßtor indexovßnφ a destruktor jsme zde deklarovali jako vlo╛enΘ metody, nebo╗ jsme zapsali jejich definiΦnφ deklarace v t∞le ╣ablony t°φdy. Pro ostatnφ metody musφme deklarovat zvlß╣tnφ ╣ablony. Podφvejme se na ╣ablonu implicitnφho a kopφrovacφho konstruktoru a operßtoru nßsobenφ Φφslem zprava:
    template<class T, int N> Pole<T,N>::Pole(T r) : delka(N) {
      p = new T[delka];
      if (!p) Chyba();
      for (int i = 0; i < N; i++) p[i] = r;
    }
    template<class T, int N> Pole<T,N>::Pole(Pole<T,N>& P): delka(P.delka) {
      p = new T[delka];
      if (!p) Chyba();
      for (int i = 0; i < N; i++) p[i] = P.p[i];
    }
    template<class T, int N> Pole<T,N> Pole<T,N>::operator*(double d) {
      Pole<T,N> Q = *this;
      for (int i = 0; i < N; i++) Q.p[i] = Q.p[i]*d;
      return Q;
    }
    Mimo deklaraci ╣ablony t°φdy musφme spolu se jmΘnem ╣ablony uvßd∞t v╛dy parametry. Jedinou v²jimkou je ╣ablona konstruktoru, kde se parametry uvßd∞jφ pouze jednou, ve jmΘnu typu vlevo od dvou dvojteΦek. Je asi jasnΘ, ╛e ╣ablony metod, kterΘ jsou deklarovßny samostatn∞, nejsou souΦßstφ deklarace ╣ablony t°φdy. MΘn∞ z°ejmΘ ale m∙╛e b²t, ╛e souΦßstφ deklarace ╣ablony t°φdy nenφ ani deklarace vlo╛enΘ sp°ßtelenΘ funkce. Proto jsme v deklaraci druhΘho operßtoru * pou╛ili zßpis Pole<T,N>.
    Vytvo°φme-li instanci ╣ablony t°φdy, vznikne objektov² typ. Pod tφm se skr²vß nejen jmΘno a struktura t°φdy, ale takΘ k≤dy metod a p°φpadnΘ instance statick²ch atribut∙. Nejjednodu╣╣φ zp∙sob jak vytvo°it instanci t°φdy je p°φmΘ pou╛itφ v deklaraci. Nap°. zßpis
    Pole<int, 15> Ppp(99);
    zp∙sobφ vytvo°enφ typu jmΘna Pole<int, 15>. SouΦasn∞ vznikne instance Ppp nov∞ vytvo°enΘho typu. Obvykle je ale v²hodn∞j╣φ vytvo°it nejprve nov² typ, tedy instanci ╣ablony, pomocφ deklarace typedef:
    typedef Pole<int, 15> intpole15;
    P°φkazem
    typedef Pole<long> lpole;
    vytvo°φme typ Pole<long, 2>, nebo╗ p°ekladaΦ pou╛ije implicitnφ hodnotu parametru N. Norma ANSI C++ umo╛≥uje p°edepsat explicitnφ generovßnφ t°φdy zßpisem
    template class Pole<int, 2>;
    KlφΦovΘ slovo class p°ed identifikßtorem je nezbytnΘ. Vyzkou╣ejte pou╛φt ╣ablonu Pole v n∞jakΘm programu.
  11. V ╣ablon∞ t°φdy m∙╛eme samoz°ejm∞ specifikovat takΘ statickΘ slo╛ky. Ka╛dß instance takovΘto ╣ablony, kterou podle tΘto ╣ablony vytvo°φme, bude obsahovat svΘ vlastnφ instance statick²ch slo╛ek. V takovΘm p°φpad∞ ov╣em pot°ebujeme pro ka╛dou instanci ╣ablony takΘ definiΦnφ deklaraci t∞chto statick²ch atribut∙; ta m∙╛e obsahovat i inicializaci. I zde si ov╣em m∙╛eme vypomoci ╣ablonou, tentokrßt ╣ablonou statickΘ slo╛ky. Nap°.

  12. template<class T> class vektor {
      static T pocet;
      T p[10];
    public:
      vektor() {for (int i=0; i < 10; i++) p[i] = 0; pocet++;}
      ...
    };
    template<class R> R vektor<R>::pocet = 0;
    // generujeme instance
    vektor<int> t;
    vektor<double> r;
    Instance vektor<int> bude mφt statick² atribut int vektor<int>::pocet; instance vektor<double> bude mφt statick² atribut double vektor<double>::pocet. Oba budou inicializovßny hodnotou 0. Instance statick²ch atribut∙ se zde vytvo°φ automaticky p°i vytvß°enφ instancφ ╣ablony vektor. ⌐ablonu statickΘ slo╛ky m∙╛eme pochopiteln∞ nahradit obyΦejnou definiΦnφ deklaracφ. To znamenß, ╛e p°edchozφ p°φklad m∙╛eme zapsat takΘ takto:
    template<class T> class vektor {
      static T pocet;
    ...
    };
    vektor<int> t;
    int vektor<int>::pocet = 0;
    vektor<double> r;
    double vektor
    Pokud nßm pro urΦitΘ hodnoty parametr∙ nevyhovuje instance vytvo°enß podle ╣ablony, m∙╛eme ji deklarovat podle sv²ch p°edstav. Mß-li p°ekladaΦ chßpat nov∞ deklarovan² typ jako instanci existujφcφ ╣ablony, musφ se jmΘno typu shodovat se jmΘnem ╣ablony a musφ obsahovat skuteΦnΘ parametry. Nap°.
    class Pole<float, 100> {
      int p[100];
    public:
      int& operator[] (int i);
      Pole<float, 100>::Pole(int m);
    };
    Pole<float, 100>::Pole(int m = -1) {
      for (int i = 0; i < 100; i++) p[i] = m;
    }
    Pro parametry float a 100 pou╛ije p°ekladaΦ instanci, kterou jsme zde p°edlo╛ili; pro ostatnφ hodnoty vytvo°φ instance podle ╣ablony.
    ⌐ablony p°edstavujφ mimo°ßdn∞ vhodn² nßstroj pro vytvß°enφ knihoven. TakΘ skladovΘ t°φdy (t°φdy p°edstavujφcφ zßsobnφky, fronty, seznamy a jinΘ struktury pro uklßdßnφ dat) se vyplatφ implementovat jako ╣ablony. ╪adu p°φklad∙ ╣ablon najdeme v BorlandskΘ knihovn∞ skladov²ch t°φd.
  13. Nynφ ji╛ jsme schopni programovat v jazyku C++. Kdy╛ ale zaΦneme vytvß°et programy pro Windows, pak zjistφme, ╛e to nenφ jednoduchΘ. API Windows (aplikaΦnφ programovΘ rozhranφ) je znaΦn∞ rozsßhlß kolekce C funkcφ. Jejich pou╛itφ si ukß╛eme na p°φkladu. Nßsledujφcφ Φßst programu zavede a zobrazφ bitovou mapu uprost°ed obrazovky:

  14. HPALETTE hPal;
    BITMAPFILEHEADER bfh;
    BITMAPINFOHEADER bih;
    LPBITMAPINFO lpbi = 0;
    HFILE hFile;
    DWORD nClrUsed, nSize;
    HDC hDC;
    HBITMAP hBitmap;
    void _huge *bits;
    do {
      if ((hFile = _lopen(data.FileName, OF_READ)) == HFILE_ERROR) break;
      if (_hread(hFile, &bfh, sizeof(bfh)) != sizeof(bfh)) break;
      if (bfh.bfType != 'BM') break;
      if (_hread(hFile, &bih, sizeof(bih)) != sizeof(bih)) break;
      nClrUsed = (bih.biClrUsed) ? bih.biClrUsed : 1 << bih.biBitCount;
      nSize = sizeof(BITMAPINFOHEADER) + nClrUsed * sizeof(RGBQUAD);
      lpbi = (LPBITMAPINFO) GlobalAllocPtr(GHND, nSize);
      if (!lpbi) break;
      hmemcpy(lpbi, &bih, sizeof(bih));
      nSize = nClrUsed * sizeof(RGBQUAD);
      if (_hread(hFile, &lpbi->bmiColors, nSize) != nSize) break;
      if (_llseek(hFile, bfh.bfOffBits, 0) == HFILE_ERROR) break;
      nSize = bfh.bfSize-bfh.bfOffBits;
      if ((bits = GlobalAlocPtr(GHND, nSize)) == NULL) break;
      if (_hread(hFile, bits, nSize) != nSize) break;
      hDC = GetDC(hWnd);
      hBitmap = CreateDIBitmap(hDC, &(lpbi->bmiHeader), CBM_INIT,
                               bits, lpbi, DIB_RGB_COLORS);
      if (hBitmap) {
        LPLOGPALETTE lppal;
        DWORD nsize = sizeof(LOGPALETTE) + (nClrUsed-1) * sizeof(PALETTEENTRY);
        lppal = (LPLOGPALETTE) GlobalAllocPtr(GHND, nSize);
        if (lppal) {
          lppal->palVersion = 0x0300;
          lppal->palNumEntries = (WORD) nClrUsed;
          hmemcpy(lppal->palPalEntry, lpbi->bmiColors, nClrUsed * sizeof(PALETTEENTRY));
          hPal = CreatePalette(lppal);
          (void) GlobalFreePtr(lppal);
        }
      }
    } while(FALSE);
    if (hFile != HFILE_ERROR) _lclose(hFile);
    HPALETTE oldPal = SelectPalette(hDC, hPal, FALSE);
    RealizePalette(hDC);
    HDC hMemDC = CreateCompatibleDC(hDC);
    HBITMAP oldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap);
    BitBlt(hDC, 0, 0, (WORD)bih.biWidth, (WORD)bih.biHeight, hMemDC, 0, 0, SRCCOPY);
    SelectObject(hMemDC, oldBitmap);
    DeleteDC(hMemDC);
    SelectPalette(hDC, oldPal, FALSE);
    ReleaseDC(hWnd, hDC);
    if (bits) (void)GlobalFreePtr(bits);
    if (lpbi) (void)GlobalFreePtr(lpbi);
    Vidφme, ╛e takov² jednoduch² problΘm jako je zobrazenφ bitovΘ mapy vy╛aduje znaΦn∞ slo╛it² k≤d. Usnadn∞nφ tΘto prßce spoΦφvß ve vytvß°enφ t°φd, kterΘ obalujφ Φasto provßd∞nΘ programovΘ ·lohy Windows, co╛ znaΦn∞ zv²╣φ produktivitu prßce programßtor∙. Po vytvo°enφ zaobalujφcφch t°φd je m∙╛eme opakovan∞ pou╛φvat. Tφm vytvß°φme pracovnφ rßmce.
    Pracovnφ rßmec je kolekce t°φd, kterΘ zjednodu╣╣ujφ programovßnφ pro Windows zaobalenφm Φasto pou╛φvan²ch technik programovßnφ. Pracovnφ rßmce naz²vßme knihovny t°φd.
    Jednφm z pracovnφch rßmc∙ je Object Windows Library (OWL) firmy Borland. S pomocφ tΘto knihovny m∙╛eme p°edchozφ Φßst programu zapsat takto:
    TDib dib("Test.bmp");
    TPalette pal(dib);
    TBitmap bitmap(dib, &pal);
    TClientDC dc(*this);
    dc.SelectObject(pal);
    dc.RealizePalette();
    TMemoryDC memdc(dc);
    memdc.SelectObject(bitmap);
    dc.BitBlt(0, 0, bitmap.Width(), bitmap.Height(), memdc, 0, 0);
    Vidφme, ╛e verze OWL je podstatn∞ krat╣φ a takΘ srozumiteln∞j╣φ. Tyto p°φklady nßm ukßzaly, co je to pracovnφ rßmec. Pracovnφ rßmec p°ed nßmi skr²vß detaily, kterΘ nemusφme znßt. Ve skuteΦnosti tato verze Φßsti programu odpovφdß prvnφ verzi, pouze n∞kterΘ Φinnosti jsou ukryty v knihovn∞ t°φd. V╣e co pot°ebujeme v∞d∞t o objektech tvo°φcφch pracovnφ rßmec je to, jak je pou╛φt v programu.
    DobrΘ pracovnφ rßmce p°ebφrajφ pln∞ mo╛nosti OOP. OWL a VCL (Visual Component Library) jsou vzorov²mi p°φklady objektov∞ orientovanΘho programovßnφ. Pracovnφ rßmce usnad≥ujφ programovßnφ. Nev²hodou je to, ╛e program zapsan² pomocφ pracovnφho rßmce je del╣φ a pomalej╣φ ne╛ stejn² program zapsan² p°φmo v C. Zv²╣enφ produktivity programovßnφ tuto nev²hodu ale p°evß╛φ.
    Jednφm z prvnφch pracovnφch rßmc∙ je OWL firmy Borland. Prvnφ verze OWL byl samostatn² produkt urΦen² pro prßci s p°ekladaΦem Borland C++ verze 3. Postupn∞ vznikaly dal╣φ verze. Nap°. od verze OWL 2.5 byla p°idßna podpora pro OLE v novΘ mno╛in∞ t°φd nazvan²ch OCF (Object Component Framework). OCF nenφ technicky souΦßstφ OWL. Pracuje na stejnΘm principu jako OWL, ale m∙╛e b²t pou╛ito nezßvisle na OWL. Poslednφ nejnov∞j╣φ verze OWL je 5.
    N∞kdy mezi OWL 1 a OWL 2 vzniklo MFC (Microsoft Foundation Class Library). MFC je souΦßstφ p°ekladaΦe Microsoft Visual C++ a je dostupnß i s p°ekladaΦi dal╣φch firem. MFC je knihovnou jinΘho typu ne╛ OWL. MFC nespl≥uje zcela po╛adavky OOP.
9. Zßklady OOP IX