AKCIA! CELOŽIVOTNÉ predplatné so zľavou 50%, nezmeškaj to!
Zisti viac
Späť na blog
Vzdelávanie

Ako na vlastný Virtuálny privátny server - časť 1.

Lubo Herkoo
21.01.2021
14 minút čítania
Ako na vlastný Virtuálny privátny server - časť 1.
Každý týždeň jeden blog. Také som si dal predsavzatie do nového roka. Hneď prvý týždeň sa to nepodarilo, ale čo už 🤦🏻‍♂️ .
V tomto texte (alebo seriáli?) budeme riešiť tvoj vlastný Virtuálny privátny server (VPS). Konkrétne:
  • zistíme čo to VPS vlastne je a aké sú výhody a prípadné nevýhody,
  • skúsime analyzovať naše potreby a vybrať vhodné parametre nášho servera,
  • VPS kúpime, nastavíme, zabezpečíme a pripravíme pre našu aplikáciu,
  • z GitHub repozitára nasadíme našu aplikáciu do produkcie pomocou GithubActions (CI),
  • kúpime doménu a nasadíme SSL certifikát pomocou letsencrypt.
Je to veľa práce, pome na to.

Načo mi je vlastný server?

Hneď na začiatku si to vyjasnime: VPS je pre väčšinu projektov zbytočný. Pri dnešných možnostiach, ako svoju aplikáciu vieme nasadiť do produkcie a sprístupniť ju používateľom na pár klikov je často zbytočné riešiť komplikované nastavovanie vlastného servera. Hlavne údržba a riešenie prípadných problémov nám môže pripomenúť, ako zle sme sa rozhodli.
Napriek tomu VPS má svoje miesto a niekedy sa naozaj môže hodiť, napríklad:
  • ak máš linux v malíčku, alebo svoj VPS už máš (si skúsený/á),
  • ak výkon zdieľaného hostingu viditeľne nestačí a už nevieš svoju aplikáciu ďalej optimalizovať,
  • ak nepostačujú dostupné technológie zdieľaného hostingu (potrebuješ doinštalovať vlastné knižnice, tooly),
  • ak má aplikácia extrémne nároky na výkon procesora, veľkosť pamäte, alebo potrebuješ ukladať gigabajty/terabajty dát,
  • ak sú dáta, s ktorými pracuješ príliž senzitívne na to, aby boli uložené na jednom mieste spolu s dátami iných používateľov zdieľaného hostingu.
Asi by sme našli aj dalšie prípady, špecifiká, kedy sa hodí VPS, tieto nám však nateraz postačia. Ak teda spadáš do niektorého z kritérií, čítaj ďalej.

Aký výkon servera potrebujem?

Brzdi. Predtým, ako pristúpime ku kúpe servera by sme mali vedieť, aké sú naše požiadavky. Aspoň približne. Potrebujeme veľa jadier procesora? Alebo si vystačíme s jedným, prípadne dvoma jadrami? Potrebujeme veľa operačnej pamäte, alebo terabajty dát na disku? To sú ťažké otázky, ale poradím ti:
  • Ak aplikácia neexistuje a chceš VPS len vyskúšať, zvoľ najlacnejší server aký je v ponuke. Či máš 1 procesor, alebo 32 procesorov - terminál reaguje vždy rovnako rýchlo.
  • Ak je aplikácia nová a nepoznáš jej nároky, začni radšej s menej výkonným a lacnejším serverom. Napríklad CPU s jedným, alebo dvoma jadrami a 2G RAM. Väčšina providerov disponuje jednoduchým škálovacím nástrojom. Ak sa zvýšia nároky, pohneš v administračnom rozhraní nejakým sliderom a šup, máš o dve jadrá viac, prípadne dvojnásobok RAM - do pár sekúnd.
  • Ak si narazil na limity zdieľaného hostingu, pravdepodobne poznáš dôvod, prečo chceš vyskúšať VPS. Najlepšie ak si komunikoval s podporou svojho providera a potvrdili, že si narazil na limit ich CPU, alebo RAM.

Kde kúpiť server?

Takže, ak vieme aspoň približne čo chceme, urobme prieskum. Osobne mám vyskúšaných týchto predajcov VPS:
  • Linode, DigitalOcean, Hetzner, WebSupport
Ubuntu Server a terminál bude u každého predajcu rovnaký. Riadiť sa teda môžeme podľa nasledovných kritérií:
  1. cena
  2. ešte raz cena
  3. možnosti administračného rozhrania a prípadného navyšovania výkonu do budúcnosti
  4. lokalita dátového centra (čo najbližšie k používateľom)

Moja skúsenosť

V poslednej dobe to u mňa osobne vyhráva Hetzner (hetzner.de). Frajeri majú vynikajúce ceny (vlastný server v čase písania tohoto textu vieš získať už za 3E/mesiac) a navyšovanie počtu CPU a RAM je riešené jednoduchým sliderom v administračnom rozhraní. Super vec 🎉. Dátové centrum môžeš zvoliť relatívne blízko - Falkenstein, Nemecko.
TIP: V ľavom hornom rohu zmeníš jazyk webu z nemeckého na anglický. 😎

Vytvorenie konta Hetzner

Ako sa už konečne teda dostaneme k vlastnému serveru? Takto:
  1. Vytvor si konto na hetzner.de (klik)
  2. Klikni na linku, ktorú ti Hetzner poslal na email zadaný pri registrácii
  3. V sérii formulárov vyplň svoje iniciály (posledný formulár vyžaduje údaje o platbe, žiadna platba vopred však nie je potrebná)
  4. Po vyplnení a odoslaní formulárov sa implicitne zobrazí formulár - nastavenia tvojich iniciál. Vpravo hore klikaj na štvorčeky a vyber z ponuky možnosť "Cloud":
Ak máš po absolvovaní predchádzajúcich krokov pred sebou takúto obrazovku:
Konzola cloudu Hetzner.de.
je všetko v poriadku.

Môj server!

Už sme blízko. V zozname projektov (predchádzajúci obrázok) klikni na "Default" (tento názov vieš zmeniť cez ikonu troch bodiek v pravom hornom rohu karty produktu) a následne "Add server".
Vytvorenie nového VPS.

Parametre servera

Teraz zvolíme parametre servera. V tomto prípade zvolíme najlacnejší variant, ale niektoré možnosti popíšeme bližšie. Parametre servera teda nastavíme nasledovne:
  1. Location (umiestnenie dátového centra): Falkenstein, pretože je najbližšie k Slovensku.
  2. Image (operačný systém): Ubuntu 20.04, pretože s ním viem pracovať a tiež existuje obrovská komunita používateľov Ubuntu servera, čo uľahčí vyhľadanie návodov a riešení prípadných problémov. So serverom Ubuntu bude ďalej pokračovať aj tento tutoriál.
  3. Type (typ servera): Štandardný, pretože nám nevadí, že spolu s našim VPS budú na fyzickom serveri bežať aj iné virutálne servery. Dedikovaný typ servera je vhodný len vtedy, ak potrebujeme naozaj veľký výpočtový výkon pre naše použitie. Z ďalších možností typu servera vyberme hneď prvý s označením CX11 a teda 1x VCPU, 2GB RAM, 20GB SSD v cene €2.99 za mesiac.
  4. Volume (externý disk): Nevytvárame externý disk. 
    • TIP: Ak vytvoríme VPS s veľkosťou SSD 20GB a potrebujeme viac dát, nemusíme hneď meniť veľkosť SSD na serveri, ale môžeme pripojiť k serveru externý disk. Má to jednu veľkú výhodu a jednu menšiu. Veľkou výhodou je, že v prípade zvyšovania výkonu (napr. z 1CPU a 2GB RAM na 4CPU 8GB RAM) môžeme zvoliť možnosť, že nechceme navyšovať aj veľkosť SSD - teda SSD ostane na hodnote 20GB. Takéto rozhodnute nám v budúcnosti umožní aj krok späť a teda zníženie počtu VCPU a RAM. Takto môžeme ušetriť nemálo finančných prostriedkov, ak potrebujeme zvýšiť výkon VPS len dočasne, nie natrvalo (napr. ak je aplikácia preťažená len v období Vianoc).
  5. Network (sieť): Nevytvárame sieť, pretože nevytvárame skupinu serverov, ktoré potrebujeme mať na jednej sieti (napr. ak by sme potrebovali zvlášť VPS pre webserver a databázový server).
  6. Additional features (ďalšie možnosti): V prípade možnosti User data nešpekulujeme (zatiaľ). To sa nám môže hodiť vtedy, ak chceme niektoré činnosti automatizovať, napríklad automaticky pridať používateľov do systému, spustiť rôzne skripty po inštalácii a podobne. Backups sú pravidelné zálohy, čo je nutnosť na produkčnom serveri, kde beží ostrá aplikácia. Táto služba je však spoplatnená - 20% z ceny nášho servera. Ak teda vytváraš produkčný server, nafurt, tak odporúčam aj so zálohami. Ak len testuješ, tak je to na tebe 😉 .
  7. SSH Key (SSH kľúč): Ak máš skúsenosti s *nix systémami, možno máš vytvorený svoj id_rsa.pub kľúč. Ak áno, tu ho môžeš použiť a tak sa autentifikovať pri pripájaní k serveru. V opačnom prípade (a to je náš prípad) ti bude zaslané heslo k root použivateľovi na tvoj email. Tu teda nešpekulujeme a zatiaľ nezaškrtneme túto možnosť. 
    • TIP: Povoliť vzdialený prístup pre root používateľa nie je bezpečné a používa sa len pre prvotné nastavenie servera (prvé prihlásenie do nového VPS). Jedným z prvých krokov po prihlásení sa do nového VPS by malo byť vytvorenie vlastného používateľa, ktorý sa bude prihlasovať pomocou kľúča (nie hesla) a zakázanie vzdialeného prístupu pre root používateľa. To bude aj náš postup.
  8. Name (Názov): Toto je názov servera, ktorý je zobrazený v administračnom paneli hetzner, ale aj v konzole po pripojení k serveru cez SSH protokol. 
    • TIP: Spomeň si na nejaké názvy svetov, postáv z tvojich obľúbených počítačových hier, komixov alebo filmov 😎 .
Veľa čítania kôli pár klikom. Nastavenie servera teda môže vyzerať aj takto:
Nastavenie parametrov VPS.

Prvé SSH spojenie

Po potvrdení nastavení chvíľu počkáme na spustenie novej inštancie nášho VPS. Zároveň nám Hetzner doručí email s informáciami o IP adrese, na ktorú sa budeme pripájať a heslom pre root používateľa. Tak skontroluj email a poď sa prihlásiť na server cez SSH.
Aký program použiť na prihlásenie cez SSH? Pre MacOS je to Terminal, alebo iTerm. V prípade linuxu (akéhokoľvek) je to veľmi podobné MacOS - čiže opäť Terminal. Používatelia Windows môžu použiť na SSH pripojenie program Putty, prípadne nainštalovať WSL doplnok a použiť WSL terminal. Napíš do komentára ak sa stretneš s nejakým problémom, poriešime.
Z emailu som sa dozvedel, ze IP môjho servera je 78.47.244.57 a heslo k používateľovi root je ss3PgfWnHwxUhUaKEEr9 (ani neskúšaj, server v dobe čítania tohto textu už nebude existovať).
Email s autorizáciou do nášho VPS.

Terminal

Príkaz ssh, ktorý použijeme v MacOS, Linux alebo WSL termináli má nasledovný syntax:
ssh pouzivatel@ip_servera

Teda v našom prípade:
ssh root@78.47.244.57

Terminál si vypýta heslo, môžeme ho len skopírovať a prilepiť. Pri zadávaní hesla do terminálu sa nezobrazujú žiadne hviezdičky ani odozva. Preto len potvrdíme príkaz klávesou Enter. Ak sa na server pripájame prvýkrát, SSH sa opýta, či chceme server uložiť do zoznamu SSH serverov. Napíšeme yes a spojenie sa v prípade správneho hesla úspešne nadviaže:
Prvé pripojenie k serveru cez SSH.
Pri prvom prihlásení je nutné zmeniť heslo používateľa root. Zadáme staré heslo a vytvoríme nové.
Hotovo. Náš nový VPS server Ubuntu 20.10 je vytvorený.

Čo ďalej?

V ďalšej časti blogu budeme pokračovať s nastavením nášho servera:
  1. vytvoríme si na lokálnom počítači SSH kľúč
  2. vytvoríme na serveri vlastného používateľa a nastavíme prihlasovanie cez SSH kľúč
  3. zabezpečíme server pomocou firewallu, fail2ban a iných nástrojov
  4. nainštalujeme a spustíme webový server (nginx)
  5. nainštalujeme a spustíme databázový server (postgresql, ak budeš potrebovať tak aj MySQL)
  6. nainštalujeme závislosti (git, nodejs, ...)
V tretej časti budeme riešiť deploy našej aplikácie na VPS:
  1. kúpime doménu a nasmerujeme ju na server
  2. naklonujeme našu aplikáciu na server, spustíme ju pod doménou a vytvoríme k nej službu (systemd service aby sa automaticky spustila pri prípadnom reštarte servera)
  3. pomocou certbot nastavíme doméne SSL certifikát a sprístupníme aplikáciu pod HTTPS
  4. pomocou GithubActions nastavime continous integration tak, aby sa po push do main branche spustili automatizovane testy a v pripade bezchybnosti sa aplikacia rovno nasadi do produkcie
Veľa roboty máme. Ale veľa sa aj naučíme.
Štvrtá časť nie je.
Lubo Herkoo
Ruby on Rails programátor, otec štyroch detí, manžel jednej manželky. V Skillmea vediem vývoj a raz za dva roky publikujem článok.

Mohlo by ťa zaujímať

Abstrakcia a zoraďovanie v kolekciách v Jave
Vzdelávanie
08.12.2020
Skillmea

Abstrakcia a zoraďovanie v kolekciách v Jave

Poďme sa porozprávať o abstraktných triedach v jave (abstract class in java). Abstrakcia slúži na schovanie zložitosti od používateľa a zobrazuje len relevantné informácie. Abstraktné triedy a metódy  V našom príklade vieme, že všetky zvieratá vydávajú zvuk. Je to niečo abstraktné – niečo, čo si vieme predstaviť a v kóde to zapíšeme nasledovne. Upravme metódu v triede Animal. public abstract void makeNoise(); Dáva to zmysel, lebo Animal nepredstavuje určité špecifické zviera a teda nevieme, aký zvuk vydá, ale vieme, že chceme aby všetko, čo bude dediť od Animal, vydávalo zvuk. Pomocou public abstract som povedal, že táto metóda nemusí mať vnútro – nemusí mať kód (implementáciu). Ak mám abstraktnú metódu, tak aj celá trieda musí byť abstract. public abstract class Animal{ public String name = "animal"; public abstract void makeNoise(); }Ak je trieda abstract, tak z nej nemôžem vyrobiť objekt. Načo by som aj robil objekt Animal, však je to len abstrakcia. Toto nebude fungovať: Animal animal = new Animal(); Ak je Animal abstract a obsahuje abstract metódu, tak som povedal, že potomk musí napísať implementáciu abstract metódy alebo bude potom tiež abstract.  Nám vyhovuje, aby Mamal bol tiež abstract. Tým pádom nemusíme vyrobiť implemntáciu. Z triedy Mamal zmažeme makeNoise a označíme ju za abstract. public abstract class Mamal extends Animal { public String name = "mamal"; }Teraz triedy, ktoré dedia od Mamal musia implementovať metódu makeNoise. Cat a Dog už danú metódu implementujú, teda nemusíme nič robiť. Ale Fox túto metódu nemá a preto ju musíme implementovať. IntelliJ IDEA nám v tomto pomôže skratkou. Stlačíme Alt + Insert a vyberieme Implement Methods. public class Fox extends Mamal { @Override public void makeNoise() { System.out.println("Ring-ding-ding-ding-dingeringeding!"); } } Zoraďovanie v kolekciáchPre zoraďovanie použijeme už existujúci algoritmus v triede Collections.sort(l). Písmeno l v tomto prípade bude List. Ak by tento List obsahoval sadu Stringov, boli by zoradené abecende, ak by obsahoval Dátum tak budú zoradene chronologicky. Ako je to možné? Je to preto, lebo tieto triedy implementujú rozhranie Comparable. Ak by si sa snažil takto zoradiť také triedy, ktoré neimplementujú toto rozhranie, tak program vyhodí výnimku.  Existuje ale možnosť, že v tvojej triede implementuješ toto rozhranie. Potom toto triedenie je teraz považované za prirodzené. Príklad: Máme Osobu, ktorá implementuje Comparable. Musíme implementovať metódu compareTo. public class Osoba implements Comparable<Osoba>{ private String meno; private String priezvisko; private int vek; public Osoba(String meno, String priezvisko, int vek) { this.meno = meno; this.priezvisko = priezvisko; this.vek = vek; } //get, set metódy vynechané pre čitatelnosť @Override public String toString() { return "Osoba{" + "meno='" + meno + '\'' + ", priezvisko='" + priezvisko + '\'' + ", vek=" + vek + '}'; } @Override public int compareTo(Osoba o) { int porovnaniePriezvisk = o.getPriezvisko().compareTo(this.getPriezvisko()); return porovnaniePriezvisk !=0 ? porovnaniePriezvisk : o.getMeno().compareTo(this.getMeno()); } } Čo ak chceš použiť úplne iné ako prirodzené triedenie, chceš to triediť napríklad podľa veku. Alebo chceš triediť objekty, ktoré neimplementujú Comparable rozhranie? Tak si ho vyrobíš. Na to použiješ rozhranie Comparator a potom ho požiješ Collections.sort(e, VEK_TRIEDENIE);. public class Sort { private static final Comparator<Osoba> VEK_TRIEDENIE = new Comparator<Osoba>() { @Override public int compare(Osoba o1, Osoba o2) { return Integer.compare(o1.getVek(), o2.getVek()); } };Vyskúšame si: public static void main(String[] args) { Osoba[] osobyArray = { new Osoba("Jaro", "Beno", 20), new Osoba("Peter", "Beno", 25), new Osoba("Karol", "Slepec", 18), new Osoba("Tomas", "Vlak", 22) }; List<Osoba> osoby = Arrays.asList(osobyArray); System.out.println(osoby); Collections.sort(osoby); System.out.println(osoby); Collections.sort(osoby, VEK_TRIEDENIE); System.out.println(osoby); } }V ďalších blogoch sa pozrieme na zopár zaujímavostí z Javy 13 a takisto sa budeme venovať aj Kotlinu. Ostaň nám verný a uč sa Javu 😊
Funkcie VLOOKUP
Vzdelávanie
16.11.2020
Skillmea

Funkcie VLOOKUP

Funkcia VLOOKUP sa používa vtedy, keď potrebujete nájsť údaje v tabuľke alebo rozsahu podľa riadka. Vyhľadajte napríklad cenu automobilovej časti podľa čísla časti alebo vyhľadajte meno zamestnanca na základe ID zamestnanca. Funkcia VLOOKUP patrí k najpraktickejším Excel funkciám. Funkcia VLOOKUP znamená: = VLOOKUP (čo chcete vyhľadať, kde ho chcete vyhľadať, číslo stĺpca v rozsahu obsahujúcom hodnotu, ktorá sa má vrátiť, vráťte približnú alebo presnú zhodu – označenú ako 1/TRUE alebo 0/FALSe). Vysvetlime si danú funkciu na jednoduchom príklade: • v databáze chceme pridať k ID klienta jeho priezvisko a mesto, z ktorého pochádza. • v jednej tabuľke (v ľavej, viď obrázok nižšie) nám chýbajú určité údaje o klientoch, konkrétne priezvisko a mesto • v druhej tabuľke (v pravej, viď obrázok) máme údaje o klientoch - jeho ID, priezvisko a mesto, z ktorého pochádza.[Image] Obidve tabuľky majú spoločný jeden identifikátor, a teda ID_klient. Na základe tejto spoločnej zhody vieme pomocou funkcie VLOOKUP vyhľadať údaje z jednej tabuľky a priradiť ich do druhej tabuľky. Riešenie: zadajme do bunky Priezvisko funkciu VLOOKUP a rozkliknime si ju. Argument funkcie Vyhľadávaná_hodnota je niečo, čo naše dve tabuľky spája, teda ID_klient. Argument funkcie Pole_tabuľky sú údaje z druhej tabuľky, z ktorej čerpáme (túto tabuľku si nezabudnite vo funkcii ukotviť pomocou klávesy F4). Ďalším argumentom fukcie VLOOKUP je Číslo_indexu_stĺpca, do ktorého napíšeme číslo stĺpca z našej druhej tabuľky - hľadáme Priezvisko, čo je druhý stĺpec, tak napíšeme číslo 2. Posledný argument funkcie je Vyhľadávanie rozsahu, ktorá môže byť približná alebo presná zhoda; TRUE (1) alebo FALSE (0). V našom prípade chceme presnú zhodu, teda použijeme 0 - FALSE. Funkcia bude zapísaná nasledovne:[Image] A tu je výsledok, podľa ID_klient sa nám zobrazia v našej prvej tabuľke priezviská zákazníkov Rovnako môžeme postupovať aj v prípade, že chceme doplniť MESTO klienta do prvej tabuľky.[Image] Krátke zhrnutie k funkcii VLOOKUP, obsahuje tieto 4 argumenty: 1. čo hľadám, 2. kde to hľadám, 3. v ktorom stĺpci je výsledok, 4. chcem hľadanú hodnotu presne alebo približne? Pokiaľ máš nejaké otázky k tejto funkcii, alebo je ti z článku niečo nejasné, napíš otázku do komentára. 
OOP v C++: trieda reprezentujúca valec a niečo viac
Vzdelávanie
05.11.2020
Skillmea

OOP v C++: trieda reprezentujúca valec a niečo viac

V úvode tohto blogu by som chcel pozdraviť všetkých nadšencov vyššieho programovacieho jazyka C++. V tomto blogu som si pre vás pripravil tému z objektovo orientovaného programovania. Z aplikačného hľadiska som si pre vás pripravil implementáciu triedy, ktorá reprezentuje valec ako 3D geometrický útvar. To znamená, že sa znova budeme baviť v intenciách analytickej geometrie. To nie je z toho dôvodu, že by som vás chcel zaťažovať vyššou matematikou, ale z toho dôvodu, že geometrické objekty sa dajú pekne reprezentovať triedami a objektami implementovanými v jazyku C++. Už v predchádzajúcom kurze som rozvinul myšlienku objektovo orientovaného programovania a okrem iného som tvrdil, že objekt v jazyku C++ odráža vlastnosti a schopnosti objektu reálneho sveta. Za takýto objekt môžeme považovať tiež valec, ktorý si nemusíte skutočne zostrojiť z nejakého materiálu, ale stačí sa naň pozerať z pohľadu analytickej geometrie.  Takže na začiatok si predstavíme trochu teórie, ktorú musíme dotiahnuť do konca práve preto, aby ste rozumeli zdrojovému kódu, ktorý som si pre vás pripravil. Potom vás ešte poverím vyriešením logickej úlohy, ktorú keď chcete úspešne splniť, musíte mať dobré poznatky zo základov analytickej geometrie. Ale o tom potom...  Je treba sa teda pripraviť a zvládnuť pojmy ako sú karteziánsky súradnicový systém, bod, vektor, priamka, rovina, ich algebrický popis, až po skalárny či vektorový súčin vektorov.  Poďme ale pekne poporiadku. Na začiatku vám položím otázku: Ako si ty predstavuješ valec v priestore? Určite by si vedel, ako vyzerá jeho tvar, poprípade by si ho vedel nakresliť. No dobre, z hľadiska geometrie môže byť, ale za tými čiarami sa skrýva aj konkrétny matematický popis. Začnime teda bodom.  Bod je základný geometrický útvar, ktorý je v 3D karteziánskom priestore reprezentovaný tromi súradnicami. Súradnice bodu môžu nadobúdať akúkoľvek hodnotu z oboru reálnych čísel. Prečo hovorím práve o bode ? Je to z toho dôvodu, aby som vám uľahčil prácu. Valec môžeme definovať práve dvoma bodmi. Konkrétne sú to body, ktoré ležia v strede dvoch podstáv valca. To znamená, že stred dolnej podstavy a stred hornej podstavy valca jasne vymedzujú výšku valca. Keď k tomu pridáme polomer podstavy, máme presnú predstavu, o ktorý valec sa jedná.  S tým uľahčením práce som to myslel naozaj vážne, asi by ste predsa nechceli, keby som vám definoval rovnicu dvoch kružníc, ktoré by reprezentovali podstavy valca, hoci aj táto reprezentácia by bola správna. K vysvetleniu reprezentácie valca, ktorá je vlastná analytickej geometrii, vám ponúkam nasledujúci obrázok:[Image] Na obrázku máte znázornený 3D karteziánsky súradnicový systém, ktorý je vlastný výpočtom v analytickej geometrie. Na okraj len podotknem, že sa jedná teda o systém, ktorý je ortogonálny a zároveň ortonormálny. V tomto súradnicovom systéme je zobrazený valec, ktorý je jednoznačne určený dvoma bodmi a polomerom podstáv valca. Spomínané dva body reprezentujú práve stredy podstáv valca.  Vráťme sa teraz na chvíľu do sveta objektovo orientovaného programovania. Už vieme, že chceme reprezentovať valec triedou. Definujme teda vlastnosti a schopnosti valca. Z kurzov viete, že vlastnosti objektu sú reprezentované dátovými členmi triedy. Ja som položil požiadavku na dátové členy triedy nasledovne:  • majme dátový člen, ktorý bude reprezentovať stred dolnej podstavy valca,  • majme dátový člen, ktorý bude reprezentovať stred hornej podstavy valca,  • majme dátový člen, ktorý bude reprezentovať polomer podstáv valca.  Uvedené tri atribúty sú dostatočné na to, aby bol valec reprezentovaný jednoznačne. Ja však kladiem ešte ďalšie požiadavky na dátové členy triedy valec. Napriek tomu, že ďalšie parametre valca sú jasne určené predchádzajúcimi tromi, chcem, aby boli explicitne dopočítané a uložené do dátových členov nasledovné údaje:  • výška valca, ktorá je jasne určená vzdialenosťou stredov podstáv,  • obsah povrchu valca, ktorý je determinovaný polomerom a výškou valca,  • objem valca, ktorý je tiež jasne určený polomerom a výškou valca.  Zatiaľ sme určili požiadavky na dátové členy. Budeme pokračovať kladením požiadaviek na druhú časť členov triedy, ktorými sú členské metódy reprezentujúce schopnosti triedy. Jednoducho povedané, požadujeme, aby mala trieda nasledujúce metódy:  • východiskový konštruktor, ktorý bude užitočný pri vytváraní novej inštancie triedy a bude inicializovať všetky dátové členy na nulové hodnoty,  • druhý používateľom definovaný konštruktor, ktorý sa tiež použije pri vytváraní novej inštancie triedy a bude inicializovať hodnoty dátových členov na argumenty odovzdávané cez parametre konštruktora. Tento konštruktor bude teda parametrický. Okrem inicializácie základných troch atribútov (stredy podstáv, polomer podstáv valca) bude dopočítavať atribúty výška, obsah a objem valca,  • deštruktor, ktorý bude dealokovať pamäť vyhradenú pre novú inštanciu tejto triedy,  • členskú metódu, ktorá bude slúžiť na prestavovanie hodnôt dátových členov, to znamená, že z valca z pôvodnými rozmermi a súradnicami vznikne iný valec, ktorý má nové súradnice a nové rozmery,  • členskú metódu, ktorá bude vracať hodnotu súradníc bodu, ktorý reprezentuje stred dolnej podstavy valca,  • členskú metódu, ktorá bude vracať hodnotu súradníc bodu, ktorý reprezentuje stred hornej podstavy valca,  • členskú metódu, ktorá bude vracať hodnotu polomeru podstáv valca,  • členskú metódu, ktorá bude vracať hodnotu obsahu povrchu valca,  • členskú metódu, ktorá bude vracať hodnotu objemu valca,  • členskú metódu, ktorá bude vracať informáciu o tom, či ľubovoľne zvolený bod definovaný tromi súradnicami patrí do priestoru (objemu valca) alebo nie.  Práve požiadavka na poslednú členskú metódu je analyticko-logická hádanka, ktorú treba vyriešiť, vyžaduje si však širšie znalosti z analytickej geometrie. Tejto úlohe sa budem teda podrobnejšie venovať a to z toho dôvodu, aby ste riešenie pochopili a nestratili sa v spleti matematických vzťahov a implementačných detailoch.  V úvode sme si teda vysvetlili, že uvediem príklad z objektovo orientovaného programovania, ktorý bude aplikovaný na oblasť analytickej geometrie a to konkrétne na geometrický objekt valec. Ďalej sme si ukázali obrázok pre jasnejšiu predstavu a pochopenie úlohy, definovali sme si požiadavky na triedu, ktorú ideme implementovať a ako posledné som zadal úlohu, ktorá rieši problém prieniku ľubovoľne zvoleného bodu a priestoru valca. Bez ďalších okolkov vám na nasledovných riadkoch ponúkam zdrojový kód, ktorý v sebe skrýva implementáciu triedy reprezentujúcu valec, triedu reprezentujúcu bod v 3D karteziánskom priestore, členskú metódu triedy valec, ktorá odpovedá na otázku, či ľubovoľný bod patrí do priestoru valca alebo nie. No a k tomu funkciu main(), v ktorej budeme novú inštanciu triedy valec používať. 001: #include <iostream> 002: #include <cmath> 003: using namespace std; 004: 005: const double pi = 3.14159; 006: 007: class cPoint3D 008: { 009: public: 010: cPoint3D(); 011: cPoint3D(double x, double y, double z); 012: ~cPoint3D(); 013: double x; 014: double y; 015: double z; 016: }; 017: 018: cPoint3D::cPoint3D() 019: { 020: this->x = 0.0; 021: this->y = 0.0; 022: this->z = 0.0; 023: } 024: 025: cPoint3D::cPoint3D(double x, double y, double z) 026: { 027: this->x = x; 028: this->y = y; 029: this->z = z; 030: } 031: 032: cPoint3D::~cPoint3D() 033: { 034: 035: } 036: 037: class cCylinder 038: { 039: public: 040: cCylinder(); 041: cCylinder(cPoint3D V1, cPoint3D V2, double radius); 042: ~cCylinder(); 043: void SetCylinder(cPoint3D V1, cPoint3D V2, double radius); 044: cPoint3D GetV1(); 045: cPoint3D GetV2(); 046: double GetRadius(); 047: double GetHeight(); 048: double GetContent(); 049: double GetVolume(); 050: bool BelongToCylinder(cPoint3D X); 051: 052: private: 053: cPoint3D V1; 054: cPoint3D V2; 055: double radius; 056: double height; 057: double content; 058: double volume; 059: }; 060: 061: cCylinder::cCylinder() 062: { 063: this->V1 = cPoint3D(0.0, 0.0, 0.0); 064: this->V2 = cPoint3D(0.0, 0.0, 0.0); 065: this->radius = 0.0; 066: this->height = 0.0; 067: this->content = 0.0; 068: this->volume = 0.0; 069: } 070: 071: cCylinder::cCylinder(cPoint3D V1, cPoint3D V2, double radius) 072: { 073: this->V1 = V1; 074: this->V2 = V2; 075: this->radius = radius; 076: this->height = sqrt(pow(this->V1.x - this->V2.x, 2.0) + pow(this->V1.y – this->V2.y, 2.0) + pow(this->V1.z - this->V2.z, 2.0)); 077: this->content = 2 * pi * pow(this->radius, 2.0) + 2 * pi * this->radius * this->height; 078: this->volume = pi * pow(this->radius, 2.0) * this->height; 079: } 080: 081: cCylinder::~cCylinder() 082: { 083: 084: } 085: 086: void cCylinder::SetCylinder(cPoint3D V1, cPoint3D V2, double radius) 087: { 088: this->V1 = V1; 089: this->V2 = V2; 090: this->radius = radius; 091: this->height = sqrt(pow(this->V1.x - this->V2.x, 2.0) + pow(this->V1.y - this->V2.y, 2.0) + pow(this->V1.z - this->V2.z, 2.0)); 092: this->content = 2 * pi * pow(this->radius, 2.0) + 2 * pi * this->radius * this->height; 093: this->volume = pi * pow(this->radius, 2.0) * this->height; 094: } 095: 096: cPoint3D cCylinder::GetV1() 097: { 098: return this->V1; 099: } 100: 101: cPoint3D cCylinder::GetV2() 102: { 103: return this->V2; 104: } 105: 106: double cCylinder::GetRadius() 107: { 108: return this->radius; 109: } 110: 111: double cCylinder::GetHeight() 112: { 113: return this->height; 114: } 115: 116: double cCylinder::GetContent() 117: { 118: return this->content; 119: } 120: 121: double cCylinder::GetVolume() 122: { 123: return this->volume; 124: } 125: 126: bool cCylinder::BelongToCylinder(cPoint3D X) 127: { 128: bool flag = true; 129: 130: double u1 = this->V2.x - this->V1.x; 131: double u2 = this->V2.y - this->V1.y; 132: double u3 = this->V2.z - this->V1.z; 133: 134: double d1 = -(u1 * X.x) - (u2 * X.y) - (u3 * X.z); 135: double t = (-(u1 * this->V1.x) - (u2 * this->V1.y) - (u3 * this->V1.z) - d1) / (u1 * u1 + u2 * u2 + u3 * u3); 136: 137: cPoint3D XT = cPoint3D(V1.x + u1 * t, V1.y + u2 * t, V1.z + u3 * t); 138: 139: double dist_01 = sqrt(pow(X.x - XT.x, 2.0) + pow(X.y - XT.y, 2.0) + pow(X.z – XT.z, 2.0)); 140: 141: double d2 = -(u1 * this->V1.x) - (u2 * this->V1.y) - (u3 * this->V1.z); 142: double d3 = -(u1 * this->V2.x) - (u2 * this->V2.y) - (u3 * this->V2.z); 143: 144: double dist_02 = abs(u1 * X.x + u2 * X.y + u3 * X.z + d2) / sqrt(u1 * u1 + u2 * u2 + u3 * u3); 145: double dist_03 = abs(u1 * X.x + u2 * X.y + u3 * X.z + d3) / sqrt(u1 * u1 + u2 * u2 + u3 * u3); 146: 147: if ((dist_01 <= this->radius) && (dist_02 <= this->height) && (dist_03 <= this->height)) 148: { 149: flag = true; 150: } 151: else 152: { 153: flag = false; 154: } 155: 156: return flag; 157: } 158: 159: int main() 160: { 161: cPoint3D V1 = cPoint3D(0.0, 0.0, 0.0); 162: cPoint3D V2 = cPoint3D(0.0, 0.0, 4.0); 163: cCylinder Cylinder = cCylinder(V1, V2, 1.5); 164: 165: cout << Cylinder.BelongToCylinder(cPoint3D(0.0, 0.0, 2.0)) << endl; 166: cout << Cylinder.BelongToCylinder(cPoint3D(0.0, 1.0, 2.0)) << endl; 167: cout << Cylinder.BelongToCylinder(cPoint3D(1.0, 1.0, 2.0)) << endl; 168: cout << Cylinder.BelongToCylinder(cPoint3D(1.5, 0.0, 0.0)) << endl; 169: cout << Cylinder.BelongToCylinder(cPoint3D(1.5, 0.0, 4.0)) << endl; 170: 171: cout << Cylinder.BelongToCylinder(cPoint3D(2.0, 1.0, 2.0)) << endl; 172: cout << Cylinder.BelongToCylinder(cPoint3D(1.0, 2.0, 2.0)) << endl; 173: cout << Cylinder.BelongToCylinder(cPoint3D(10.0, 5.0, 7.0)) << endl; 174: 175: cout << endl; 176: 177: V1.~cPoint3D(); 178: V2.~cPoint3D(); 179: Cylinder.~cCylinder(); 180: 181: return 0; 182: }Na riadku 001 je zavolaná direktíva preprocesora #include, ktorá pridáva hlavičkový súbor iostream do zdrojového kódu. Časť tejto štandardnej knižnice potrebujeme, pretože chceme používať objekt cout, pomocou ktorého budeme zapisovať informáciu do okna konzolovej aplikácie. Podobne je na riadku 002 opäť použitá direktíva preprocesora #include. Tentokrát však kvôli pridaniu hlavičkového súboru cmath. Tento hlavičkový súbor je tiež súčasťou štandardnej knižnice jazyka C++ a potrebujeme ho, pretože budeme používať funkcie pow() a sqrt(). Len pre ozrejmenie funkciu pow() budeme používať pre výpočet druhej mocniny nami zadaného argumentu a funkciu sqrt() pre výpočet druhej odmocniny zadaného argumentu.  Na riadku 003 pomocou kľúčového slova using definujeme, že budeme používať menný priestor std. Tento riadok som do kódu umiestnil preto, aby sme nemuseli ku objektom, triedam a funkciám, ktoré do tohto priestoru patria, pristupovať cez std a operátor prístupu :: . Okrem toho, že funkcie pow(), sqrt(), objekt cout a manipulátor endl prislúchajú nejakým hlavičkovým súborom, patria aj do menného priestoru std.  Na riadku 005 je definovaná konštanta s identifikátorom pi. Tá reprezentuje Ludolfovo číslo a má hodnotu 3,14159. Túto konštantu potrebujeme k výpočtu obsahu povrchu a objemu valca. Pokračujeme riadkom 007, na ktorom pomocou kľúčového slova class deklarujeme triedu s identifikátorom cPoint3D. Táto trieda nám bude v programe reprezentovať bod v 3D karteziánskom súradnicovom systéme. Deklaráciu spomínanej triedy začíname na riadku 008 ľavou programovou zátvorkou, za ktorou na riadku 009 nasleduje kľúčové slovo public, ktorým definujeme, že nasledovným členom bude pridelený verejný prístup.  Na riadku 010 až 012 sú teda deklarované tri členské metódy triedy cPoint3D, pričom sú to špeciálne členské funkcie a to konkrétne dva konštruktory a deštruktor.  Na riadku 010 je deklarovaný východiskový konštruktor, ktorému neprislúchajú žiadne parametre. Tento konštruktor slúži na vytvorenie novej inštancie triedy cPoint3D s tým, že inicializuje dátové členy na nulové (východiskové) hodnoty.  Na riadku 011 je deklarovaný používateľom definovaný konštruktor, ktorý slúži tiež na vytvorenie novej inštancie triedy, pričom pomocou svojich parametrov, cez ktoré sa odovzdávajú konkrétne argumenty (súradnice bodu), inicializuje dátové členy triedy. Rozdiel medzi prvým a druhým konštruktorom je ten, že prvý nepotrebuje parametre, pretože inicializuje dátove členy na 0, ktoré je možné potom prestaviť inou členskou metódou na konkrétne hodnoty, zatiaľ čo druhý konštruktor priraďuje dátovým členom nenulové hodnoty prostredníctvom argumentov odovzdávaných cez parametre konštruktora. K úplnosti textu uvádzam, že druhý konštruktor má tri parametre x, y, a z práve preto, že bod v 3D priestore má tri súradnice a k tomu prislúchajú tiež tri členské premenné triedy cPoint3D. Sú to premenné s tým istým identifikátorom ako parametre konštruktora, čiže x, y, z. Existujú informačné zdroje, ktoré spochybňujú a neodporúčajú totožnosť identifikátorov parametrov konštruktorov a členských premenných triedy. Ja však toto názvoslovie považujem za bežné a správne z toho dôvodu, že existuje kľúčové slovo this, ktoré reprezentuje vo vnútri objektu ukazovateľ na objekt a možno prostredníctvom neho pristupovať k dátovým členom. Týmto zápisom (syntaxou) sa dá odlíšiť identifikácia parametra a členskej premennej objektu. Je predsa jasný rozdiel medzi x a this->x. Prvé je parameter konštruktora a druhé dátový člen, ku ktorému pristupujeme pomocou ukazovateľa.  Na riadku 012 je deklarovaný deštruktor, ktorý nám bude slúžiť na dealokáciu pamäte  vyhradenej pre novú inštanciu triedy cPoint3D po jej vytvorení. Deštruktory sú špeciálne členské funkcie tried, ktoré slúžia práve na spomenutý účel, jednoducho povedané, majú za úlohu upratať po objekte v pamäti.  Na riadkoch 013 až 015 sú deklarované tri dátové členy x, y a z, ktorým je pridelený typ double. Tieto reprezentujú súradnice bodu v 3D karteziánskom súradnicovom systéme. Na riadku 016 sa uzatvára deklarácia triedy cPoint3D pravou programovou zátvorkou a bodkočiarkou. Len pripomeniem, že bodkočiarka tam musí byť uvedená, je to jeden z ojedinelých prípadov v jazyku C++, kedy sa používa bodkočiarka za programovou zátvorkou.  Na riadku 018 začína definícia východiskového konštruktora triedy cPoint3D. Jedná sa o bezparametrický konštruktor, ktorý inicializuje dátové členy na riadkoch 020 až 022 na nulu. Po tomto priradení sa konštruktor ukončuje pravou programovou zátvorkou na riadku 023.  Na riadku 025 začína definícia druhého používateľom definovaného konštruktora tej istej triedy, ktorý na rozdiel od prvého má tri parametre, prostredníctvom ktorého sa odovzdávajú argumenty reprezentujúce súradnice bodu v 3D karteziánskom súradnicovom systéme. Tieto sa na riadku 027 až 029 priraďujú jednotlivým dátovým členom. Konštruktor sa na riadku 030 končí pravou programovou zátvorkou.  Na riadku 032 až 035 je uvedená definícia deštruktora triedy cPoint3D. Jeho telo je prázdne a neobsahuje žiadny kód tak, ako to býva u väčšiny deštruktorov. Pre zopakovanie úlohou deštruktora je dealokovať (uvoľniť) pamäť, ktorá bola konkrétnej inštancii triedy vyhradená.  V zdrojovom kóde sa na riadku 037 ďalej pokračuje deklaráciou triedy cCylinder, ktorá nám bude reprezentovať práve geometrický objekt valec. Na riadku 038 je uvedená ľavá programová zátvorka, ktorá otvára spomínanú deklaráciu. Za ňou je uvedené kľúčové slovo public, ktoré slúži na pridelenie verejného prístupu k dátovým členom a členským metódam, ktoré sú ďalej deklarované. Zatiaľ čo trieda cPoint3D mala len verejné dátové členy a členské metódy, trieda cCylinder obsahuje aj členy súkromné. To znamená, že pri tejto triede budeme využívať techniku skrývania niektorých dátových členov. Obor platnosti verejného prístupu platí v zdrojovom kóde po kľúčové slovo private, čiže po kľúčové slovo, ktoré nastavuje iný typ prístupu k členom triedy. Pre úplnosť informácie dodám, že členom triedy môže byť pridelený ešte tretí typ prístupu protected, čo v preklade znamená chránený prístup. Tento typ prístupu však v tejto úlohe využívať nebudeme.  Vráťme sa ale naspäť ku zdrojovému kódu. Na riadkoch 040 až 050 sú deklarované všetky členské metódy, ktorým je pridelený verejný prístup. Tieto si postupne rozoberieme.  Na riadku 040 je uvedená deklarácia východiskového konštruktora, ktorý má za úlohu inicializovať všetky dátové členy triedy na nulové hodnoty. Ako poznámku uvediem, že nevidím veľmi zmysel týchto konštruktorov, ale v praxi sa volí práve spomínaný postup. To znamená, že sa vytvoria dva konštruktory, jeden bez parametrov, ktorého funkcionalita bola spomenutá vyššie a druhý konštruktor s parametrami, cez ktoré môžete dátovým členom priradiť už konkrétne nenulové hodnoty. Ku východiskovému konštruktoru je potrebné potom doprogramovať implementáciu metódy (tzv. setter), ktorá má rovnakú funkciu ako konštruktor s parametrami. Rozdiel oproti použitiu parametrického konštruktora je ten, že inicializujete dátové členy triedy v dvoch krokoch. V prvom kroku definujete objekt s inicializáciou dátových členov východiskovými nulovými hodnotami a v druhom kroku sa potom tieto členy inicializujú na používateľom zadanú hodnotu. Pre úplnosť doplním, že zmysel používania východiskových konštruktorov vidím pri pointrovej aritmetike, to znamená v prípadoch, kedy sú dátové členy deklarované ako smerníky základných vstavaných typov alebo typov, ktoré sú definované používateľom. Vtedy slúži východiskový konštruktor okrem vytvorenia objektu na alokáciu pamäti (konkrétne haldy) pre daný typ pomocou operátora new.  Na riadku 041 je deklarovaný druhý konštruktor, ktorý má tri parametre. Prvý a druhý parameter sú typu cPoint3D, tie reprezentujú súradnice stredov podstáv valca. Tretí parameter reprezentuje polomer podstáv valca. To sú tri parametre, pomocou ktorých je možné jednoznačne definovať valec.  Na riadku 042 je deklarovaný deštruktor triedy cCylinder. Takisto ako u predošlej triedy má za úlohu dealokovať pamäť, ktorá je vyhradená pre novú inštanciu tejto triedy.  Pokračujeme deklaráciou členskej metódy SetCylinder() na riadku 043. Jedná sa o tzv. setter, čiže metódu, ktorá má za úlohu nastaviť hodnoty členských dát triedy.  Na riadku 044 a 045 sú deklarované členské metódy GetV1() a GetV2(). Tieto metódy nemajú žiadne parametre a vracajú typ cPoint3D. Úlohou prvej z nich je vracať informáciu o súradniciach stredu dolnej podstavy valca, druhá z nich vracia informáciu o súradniciach stredu hornej podstavy valca.  Na riadkoch 046 až 049 sú deklarované členské metódy GetRadius(), GetHeight(), GetContent() a GetVolume(). Sú to getter-y, ktoré majú návratový typ double. V takom poradí ako boli vymenované, slúžia na to, aby vracali hodnotu členských dát, ktoré reprezentujú polomer, výšku, obsah a objem valca.  Na riadku 050 je deklarovaná členská metóda BelongToCylinder(). Ako návratový typ vracia bool. Táto metóda slúži na zistenie toho, či ľubovoľne zvolený bod v 3D karteziánskom súradnicovom priestore patrí do priestoru (objemu) valca alebo nie. Podotýkam, že vyriešenie tejto logickej úlohy je najnáročnejšie, pretože musíte poznať hlbšie súvislosti z analytickej geometrie. Ostatná implementácia triedy cCylinder je pomerne jednoduchá záležitosť oproti spomínanej úlohe.  Na riadku 052 je uvedené kľúčové slovo jazyka C++ private, ktoré zabezpečí, že ďalej deklarované dátové členy budú mať súkromný prístup. Na riadkoch 053 a 054 sú potom deklarované dátové členy V1 a V2, ktoré reprezentujú stred dolnej a hornej podstavy valca. Práve preto im bol pridelený údajový typ cPoint3D. Ďalej sú na riadkoch 055 až 058 deklarované dátové členy radius, height, content a volume. Je im pridelený dátový typ double a v tom istom poradí reprezentujú polomer, výšku, obsah a objem valca.  Na riadku 059 je uvedená pravá programová zátvorka a s bodkočiarkou, ktoré uzatvárajú deklaráciu používateľom definovaného typu (triedy) cCylinder. Implementačné detaily (definícia) tejto triedy pokračuje na ďalších riadkoch zdrojového kódu.  Na riadkoch 061 až 069 je uvedená definícia východiskového konštruktora cCylinder. Konštruktor nemá žiadne parametre, jeho úlohou je vytvárať novú inštanciu triedy cCylinder a inicializovať všetky dátové členy tejto triedy na nulové hodnoty. Na riadkoch 071 až 079 je uvedená definícia druhého používateľom definovaného konštruktora, ktorá má tri parametre. Prvé dva sú typu cPoint3D, pomocou nich sa odovzdávajú súradnice stredov dolnej a hornej podstavy valca. Cez tretí parameter, ktorý je typu double, sa odovzdáva hodnota polomeru valca. Dátovým členom V1, V2 a radius sú priamo priradené hodnoty prislúchajúcich parametrov konštruktora. Hodnota dátového člena height, ktorá reprezentuje výšku valca, je vypočítaná zo súradníc bodov podstáv. Výška valca sa totiž rovná vzdialenosti týchto bodov, ktorá je geometricky daná ako druhá odmocnina súčtu mocnín rozdielu jednotlivých súradníc dvoch bodov. Vzťah pre výpočet vzdialenosti dvoch bodov je nasledovný:[Image] Hodnota dátového člena content, ktorý reprezentuje obsah povrchu valca, je determinovaný výškou valca a polomerom podstavy valca. Vzťah pre výpočet obsahu povrchu valca je nasledovný:[Image] Podľa uvedeného vzťahu sa vypočíta obsah povrchu valca, ktorého hodnota je priradená do členskej premennej content na riadku 077. Na riadku 078 je vypočítaná hodnota objemu valca a priradená do členskej premennej volume. Objem valca je determinovaný tiež polomerom a výškou valca podobne, ako tomu bolo pri výpočte obsahu povrchu valca. Vzťah pre výpočet objemu valca je nasledovný:[Image] Na riadku 079 je uvedená pravá programová zátvorka, ktorá uzatvára definíciu používateľom definovaného konštruktora triedy cCylinder. Na riadku 081 až 084 je uvedená definícia deštruktora triedy cCylinder. Telo deštruktora je prázdne, jeho úlohou je dealokovať pamäť, ktorá bola vyhradená pre novú inštanciu triedy cCylinder.  Na riadku 086 až 094 je uvedená definícia členskej metódy SetCylinder, ktorá má návratový typ void, čiže vracia absenciu informácie. Táto metóda je tzv. setter, ktorý má za úlohu nastaviť (prestaviť) hodnoty dátových členov triedy cCylinder. Jeho funkcionalita je rovnaká ako u používateľom definovaného konštruktora s tým rozdielom, že táto metóda nie je volaná pri vytváraní novej inštancie. Môže byť však aplikovaná na inštanciu, ktorá už bola vytvorená. Pri jej volaní sa zmenia parametre valca, čiže možno ňou definovať iný valec v 3D karteziánskom priestore.  Na riadku 096 až 099 je definovaná členská metóda GetV1(). Má návratový typ cPoint3D, pričom je to getter, ktorý má za úlohu vrátiť súradnice stredu dolnej podstavy valca, ktorý je objektom triedy cCylinder reprezentovaný.  Na riadku 101 až 104 je definovaná členská metóda GetV2(). Má tiež návratový typ cPoint3D, a podobne ako predošlá metóda má za úlohu vrátiť súradnice stredu podstavy valca, v tomto prípade sa však jedná o hornú podstavu.  Na riadku 106 až 109 je definovaná členská metóda GetRadius(). Vracia návratový údajový typ double, ktorý reprezentuje polomer podstavy valca.  Na riadku 111 až 114 je definovaná členská metóda GetHeight(). Jej úlohou je vrátiť hodnotu dátového člena height, ktorý reprezentuje výšku valca.  Na riadku 116 až 119 je definovaná členská metóda GetContent(). Táto metóda má za úlohu vrátiť hodnotu dátového člena content, ktorý reprezentuje vypočítaný obsah povrchu valca.  Na riadku 121 až 124 je definovaná členská metóda GetVolume(). Táto metóda má za úlohu vrátiť hodnotu dátového člena volume, ktorý reprezentuje vypočítaný objem valca.  Na riadkoch 126 až 157 je definovaná členská metóda BelongToCylinder(). Úlohou tejto metódy je rozhodnúť o tom, či bod zadaný cez parameter patrí do priestoru (objemu) valca alebo nie. Návratový typ tejto metódy je bool, ktorý reprezentuje práve pravdivostnú hodnotu spomínaného rozhodnutia. Ak bod so súradnicami zadanými cez parameter metódy patrí do priestoru valca, metóda vracia hodnotu true, ak nie, vracia hadnotu false. Na prvý pohľad sa zadaná úloha zdá jednoduchá, nie je tomu však tak. Práve preto uvediem nasledovné predpoklady, ktoré budeme potom analytickým postupom (výpočtom dokazovať):  • ak bod patrí do priestoru valca, musí byť kolmá vzdialenosť tohto bodu od osi valca menšia nanajvýš rovná polomeru podstavy valca,  • ak bod patrí do  priestoru valca, musí byť kolmá vzdialenosť tohto bodu od jeho dolnej podstavy menšia nanajvýš rovná výške valca,  • ak bod patrí do  priestoru valca, musí byť kolmá vzdialenosť tohto bodu od jeho hornej podstavy menšia nanajvýš rovná výške valca.  K uvedeným predpokladom ešte dodám, že nestačí splnenie jedného alebo dvoch z nich. Ak chceme potvrdiť výrok, že konkrétny bod patrí do priestoru valca, musia byť splnené všetky tri uvedené podmienky. Úspešné vyriešenie úlohy, má nasledovný postup: 1. Vypočítame súradnice smerového vektora priamky, ktorá prechádza stredmi obidvoch podstáv valca. Tejto priamke hovoríme os valca, ktorá je jasne definovaná spomínanými dvoma bodmi (stred dolnej a hornej postavy valca). Súradnice vypočítame odčítaním dvoch bodov, čo znamená, že spravíme rozdiel príslušných súradníc bodov podľa nasledovných vzťahov:[Image] 2. Ďalej zostrojíme rovinu, ktorá je kolmá na os valca a zároveň prechádza bodom, o ktorom rozhodujeme, či patrí do priestoru valca alebo nie. Keďže smerový vektor osi valca je totožný s normálovým vektorom spomínanej roviny, vieme vyjadriť koeficienty a, b, c, ktoré sú súčasťou všeobecného tvaru rovnice roviny. Z uvedeného vyplýva, že potrebujeme dopočítať už len koeficient d všeobecného tvaru rovnice roviny. Všeobecný tvar rovnice priamky je teda nasledovný:[Image] Keď z uvedenej rovnice vyjadríme koeficient d, dostaneme nasledovný matematický vzťah: [Image] Po vypočítaní koeficientu d, pokračujeme nasledovným krokom, v ktorom počítame súradnice bodu, ktorý vznikne prienikom osi valca a rovinou, ktorá je na ňu kolmá a prechádza bodom, o ktorom rozhodujeme, či patrí do priestoru valca alebo nie. 3. Majme teda os valca (priamku), ktorá je daná nasledovným parametrickým vyjadrením[Image] Po rozvinutí pre jednotlivé súradnice, vzniknú nasledovné rovnice:[Image] Keďže počítame prienik priamky a roviny, dosaďme rovnice 3.2, 3.3 a 3.4 do rovnice 2.1, vznikne nám nasledovný matematický vzťah.[Image] Keďže koeficienty a, b, c sú súradnice smerového vektora osi valca, môžeme napísať:[Image] Po úprave rovnice 3.6 a vyjadrení parametra t, dostaneme nasledovný vzťah:[Image] 4. Po vypočítaní koeficientu t, môžeme vypočítať súradnice bodu, ktorý vznikne prienikom osi valca a spomínanej roviny podľa nasledovných vzťahov[Image] 5. Označme bod, o ktorom rozhodujeme, či patrí do priestoru valca identifikátorom A, ďalej označme bod, ktorý vznikne prienikom osi valca a spomínanej roviny ako bod B, vzdialenosť týchto dvoch bodov potom vypočítame podľa nasledovného vzťahu[Image] Ako už bolo povedané, ak je táto vzdialenosť menšia nanajvýš rovná polomeru podstavy valca, máme splnenú prvý predpoklad, že zadaný bod patrí do priestoru valca. 6. V tomto kroku budeme pokračovať analytickým spôsobom, ktorým budeme dokazovať, že bod spĺňa druhý predpoklad, to znamená, že vzdialenosť bodu od dolnej podstavy valca je menšia ako výška valca. Budeme vychádzať zo vzťahu 2.2 a vypočítame koeficient d pre rovinu, ktorá je kolmá na os valca, prechádza však stredom dolnej podstavy valca. Táto rovina má rovnaký normálový vektor ako rovina, ktorá prechádzala tým bodom, o ktorom rozhodujeme, či patrí do priestoru valca alebo nie. Koeficienty a, b, c, ktoré vystupujú vo všeobecnom tvare rovnice roviny, prislúchajú teda hodnotám súradníc smerové vektora osi valca. Z uvedené vyplýva, že keď poznáme súradnice normálového vektora roviny a poznáme bod, ktorý v nej leží (stred dolnej podstavy valca), vieme vyčísliť hodnotu koeficienta d. 7. Po predchádzajúcom kroku nám už stačí dopočítať vzdialenosť bodu od podstavy priamym vzťahom, ktorý je nasledovný:[Image] Ak je táto vzdialenosť menšia nanajvýš rovná výške valca, máme splnený druhý predpoklad, aby sme mohli tvrdiť, že bod patrí do priestoru valca. 8. Pokračujeme výpočtom vzdialenosti bodu od hornej podstavy valca. Zostrojíme teda rovinu, ktorá je kolmá na os valca a prechádza stredom hornej podstavy valca. K tomuto prídeme rovnakým spôsobom ako v kroku 6. Obdobne podľa vzťahu 7.1 vypočítame predmetnú vzdialenosť. Ak je táto menšia nanajvýš rovná výške valca, máme splnený posledný predpoklad, pre tvrdenie výroku, že zadaný bod patrí do priestoru valca. Práve tento postup je zachovaný pri implementácii riadkov zdrojového kódu 128 až 156. Na označenie jednotlivých parametrov, ktoré boli súčasťou priebežného výpočtu sa len použili v niektorých prípadoch iné identifikátory.  Na riadku 128 je deklarovaná premenná typu bool s identifikátorom flag. Do nej sa uloží informácia, ktorá bude reprezentovať pravdivostnú hodnotu výroku o tom, či bod do priestoru valca patrí alebo nie. Na začiatku ju inicializujeme hodnotou true.  Na riadku 130 je deklarovaná premenná u1, ktorá reprezentuje x zložku smerového vektora osi valca, vypočítaná je ako rozdiel x súradníc bodu V2 a V1.  Na riadku 131 je deklarovaná premenná u2, ktorá reprezentuje y zložku smerového vektora osi valca, vypočítaná je ako rozdiel y súradníc bodu V2 a V1.  Na riadku 132 je deklarovaná premenná u3, ktorá reprezentuje z zložku smerového vektora osi valca, vypočítaná je ako rozdiel z súradníc bodu V2 a V1. Pre doplnenie informácie, identifikátory V1 a V2 patria dátovým členom triedy cCylinder, ktoré reprezentujú súradnice stredov podstáv valca.  Na riadku 134 je vypočítaná hodnota premennej d1, ktorá reprezentuje koeficient d vo všeobecnom tvare roviny, ktorá je kolmá na os valca a prechádza bodom X (bod, o ktorom rozhodujeme). Tento koeficient je potrebný k tomu, aby bola kompletná rovnica roviny v priestore. Potom je možné vypočítať parameter t podľa vzťahu 3.7. Práve tento vzťah modeluje priraďovací príkaz na riadku 135.  Po výpočte parametra t môžeme definovať bod XT (bod, ktorý je kolmým priemetom bodu X na os valca), ktorý je typu cPoint3D. Ten je definovaný na riadku 137 tak, že sa zavolá konštruktor triedy cPoint3D, ktorému sa odovzdajú hodnoty argumentov. Tieto hodnoty sa vypočítavajú podľa vzťahov pre výpočet súradníc bodu ležiaceho na priamke, ktorá je daná svojim parametrickým vyjadrením (vzťahy 4.1 až 4.3).  Na riadku 139 je deklarovaná premenná dist_01, ktorej sa priraďuje výpočet vzdialenosti bodu X od osi valca. Tá sa vypočíta ako geometrická vzdialenosť dvoch bodov v 3D karteziánskom priestore, a to konkrétne bodu X a XT podľa vzťahu 5.1.  Na riadku 141 a 142 sú vypočítané hodnoty premenných d2 a d3, ktoré reprezentujú koeficient d rovín, ktoré prechádzajú stredmi podstáv valca a sú kolmé na os x. Z týchto koeficientov, smerového vektora a súradníc bodov X sa vypočítavajú vzdialenosti bodu od hornej a dolnej postavy valca. Tieto vzdialenosti sa v zdrojovom kóde počítajú na riadkoch 144 a 145.  Na riadku 147 je uvedený príkaz if, v ktorom je vyhodnotená pravdivosť výrazu, ktorý je tvorený tromi jednoduchými výrokmi. Prvý z nich tvrdí, že vzdialenosť bodu od osi valca je menšia alebo rovná polomeru valca. Druhý z nich tvrdí, že vzdialenosť bodu od dolnej podstavy valca je menšia alebo rovná výške valca. A posledný z výrokov obdobne tvrdí, že vzdialenosť bodu od hornej podstavy valca je menšia alebo rovná ako výška valca. Výroky sú pospájané logickým operátorom and, čo znamená, že ak sú súčasne všetky pravdivé, zložený výrok je tiež pravdivý a riadenie programu prejde na riadok 149, kde sa hodnote flag priradí pravdivostná hodnota true. Ak je čo len jeden z troch výrokov nepravdivý, zložený výrok je nepravdivý tiež, čo znamená, že bod nepatrí do priestoru valca a riadenie programu prejde na riadok 153 do klauzuly else, kde sa priradí hodnote flag pravdivostná hodnota false. Na riadku 156 sa vracia hodnota premennej flag pomocou kľúčového slova return.  Na riadku 159 sa začína definícia funkcie main(), ktorú volá operačný systém. V tele tejto funkcie sú na riadku 161 a 162 vytvorené nové inštancie triedy cPoint3D, ktoré reprezentujú stredy podstáv valca. Na riadku 163 je vytvorená nová inštancia triedy cCylinder, kde sa pomocou parametrov konštruktora odovzdávajú argumenty reprezentujúce stredy podstáv a polomer valca. Na riadkoch 165  až 173 sú do okna konzolovej aplikácie zapísané informácie o tom, či body, ktorých súradnice zadané ako argumenty členskej metódy BelongToCylinder(), patria do priestoru valca alebo nie. Celkovo sme volali túto členskú v zdrojovom kóde 8-krát. To znamená, že sme preverovali osem bodov. Len pre informáciu prvých päť patrilo do priestoru valca a ďalšie tri nie. Pre správnosť programu si výsledky môžete overiť analytickým postupom, ktorý je v predchádzajúcom texte uvedený.  Na riadku 175 sa len prechádza v okne konzolovej aplikácie na ďalší riadok. Na riadkoch 177 až 179 sú už len zavolané deštruktory definovaných objektov z dôvodu dealokácie pamäte. Na riadku 181 je vrátená hodnota 0 operačnému systému. Na riadku 182 sa ukončuje telo funkcie main() pravou programovou zátvorkou. V závere vám chcem poďakovať za prečítanie blogu. Dúfam, že vás článok, ktorý sa týkal objektovo orientovaného programovania a príkladu z analytickej geometrie zaujal. Podstatnou pridanou hodnotou zdrojového kódu bola členská funkcia, ktorá rozhodla o tom, či bod zadaný ako argument tejto funkcie patrí do priestoru valca alebo nie. Bola to skutočne náročnejšia úloha a preto som rád, že ste riešenie pochopili a spolu so mnou túto úlohu rozlúskli. Autorom tohto článku je Marek Šurka.

Nezmeškaj info o nových kurzoch a špeciálnych ponukách