Programmeerimine keeles C++
Praktikum 3: mallid, C++ standardteegi konteinerid, iteraatorid
Ülesanded
Üldised tingimused
Tähtis! Loe läbi ülesannete vormistamise tingimused aine veebilehelt! Ülesandeid esitatakse läbi aine veebilehel asuva vormi. E-posti teel lahendusi vastu ei võeta. Küsimustega pöörduda aine listi või praktikumijuhendaja poole.
Lahendamiseks on aega 7 päeva.
Tähtaeg: 25.03.2018 23:59:59
Ülesanne 1 – Geomeetria suvalise mõõtmete arvuga ruumis
Selles praktikumis kirjutame seni kõige täiuslikumad punkti, sirglõigu ja kera klassid. Nimelt kasutame klassimalle, et luua geomeetrilisi objekte suvalise mõõtmega ruumis (märkus: ma palun, et teie kood annaks triviaalseid vastuseid ka siis, kui parameetritega määratakse 0-mõõtmeline ruum). Järgmiste klasside realiseerimiseks kasutage malle.
Kui teil ei teki cpp-faile, siis ärge tehke ka geometry-teegile .a faili. Esimeses ülesandes võib nii juhtuda kergesti.
1.1. Universaalne punkt (point.h
) (3 punkti)
Looge klass Point
, millele saab ette anda koordinaatide arvu (template <unsigned short n>
). Etteantav mittenegatiivne täisarv n määrab, mitu murdarvulist mõõdet vektoril on. Punkti koordinaate salvestage STL konteinerklassi std::list
abil klassi muutujas nimega coords
.
Mõned näited:
Point<2> kahem66tmelinePunkt; // punkt tasandil Point<10> kymnem66tmelinePunkt; // punkt kümnemõõtmelises ruumis
Lisage klassile meetodid:
Meetod | Eesmärk |
---|---|
Point<n> () | algväärtustab koordinaadid nullidega |
Point<n> (list<float> crds) | väärtustab vektori koordinaadid etteantud väärtustega |
distanceFrom (Point<n> v) | tagastab kauguse teisest sama mõõtmehulgaga punktist |
string toString () | tagastab vektori esituse sõnena (x1, x2, ..., xn) (kasuta funktsiooni to_string ) |
operator << | väljastab vektori andmed (kasuta ära toString meetodit) |
Väljundvoogu nihutamise operaatori ülelaadimisel on abiks friend
võtmesõna.
NB! Operaatori ülelaadimist on lihtsam realiseerida, kui parameetriks antud Point
objekt pole konstant. Ilusama lahenduse kirjutamiseks vaata vihjeid viimasel leheküljel.
Juhul, kui tekivad veaolukorrad, näiteks kui malliga määratud koordinaatide arv n erineb etteantud koordinaatide vektori suurusest, visake string-tüüpi erind (vt materjali). Erindi teksti kirjutage inimkeelne selgitus selle kohta, mis toimus. Arvestage, et 0-koordinaadiga vektor on põhimõtteliselt täiesti lubatud ning seda veajuhuks lugema ei peaks.
1.2. Universaalne sirglõik (line.h
) (1 punkt)
Looge klassimall Line
, mille parameetriks on punkti klass T
(template <class T>
). Klass esindab sirglõike üle suvalise mõõtme punktide. Klassil on kaks muutujat – p1
ja p2
, mõlemad on tüüpi T
– need esindavad sirglõigu tippe. Tulemusena peab saama teha kahe tipuga sirglõike nii:
Line< Point<2> > kahem66tmelineSirgl6ik; Line< Point<7> > seitsmem66tmelineSirgl6ik;
Lisage klassile meetodid:
Meetod | Eesmärk |
---|---|
Line<T> () | vaikekonstruktor – loob tipud (T vaikekonstruktoriga) |
Line<T> (T np1, T np2) | parameetritega konstruktor – väärtustab klassi elemendid |
float length () | tagastab sirglõigu pikkuse kasutades klassi T meetodit distanceFrom |
string toString () | tagastab lõigu esituse sõnena ((tipp1)-(tipp2)) |
operator << | väljastab lõigu andmed (kasuta ära toString meetodit) |
1.3. Universaalne kera (sphere.h
) (3 punkti)
Looge klassimall Sphere
, mille parameetriks on punkti klass T
(template <class T>
). Klass esindab kerasid (ja kahemõõtmelisel erijuhul ringe). Klassil olgu T
tüüpi muutuja o
, mis esitab keskpunkti ja float
-tüüpi murdarv r
, mis esitab raadiust. Raadius ei tohiks olla kunagi negatiivne.
Tulemusena peab saama teha ringe ja kerasid nii:
Sphere< Point<2> > ring; Sphere< Point<3> > kera;
Meetod | Eesmärk |
---|---|
Sphere<T> () | vaikekonstruktor – loob T tüüpi tipu ja paneb raadiuseks nulli |
Sphere<T> (T no, float nr) | parameetritega konstruktor – kasutab antud tippu ja raadiust |
bool contains (T v) | tagastab true , kui tipp on kera pinnal või sees, muidu false |
bool contains (Line<T> l) | tagastab true , kui antud lõik on kera sees, muidu false |
void scale (float factor) | korrutab kera raadiuse antud väärtusega |
string toString () | tagastab kera esituse sõnena ((tipp), raadius) |
operator << | väljastab kera andmed (kasuta ära toString meetodit) |
See ei ole ülesande osa, aga jätke meelde, et keeles on olemas ka võimalus konkreetsete parameetritega implementatsioonide kirjutamiseks. Näiteks võite realiseerida erijuhtudena kahemõõtmelise kera (ringi) jaoks ümbermõõdu ja pindala ning kolmemõõtmelise kera jaoks ruumala arvutamise. Vastav märksõna, mida uurida, on template specialization.
NB! Ärge muutke soovitatud muutujanimesid ning hoidke muutujad avalikena. Nii on testimine lihtsam. Samuti tehke päisefail geometry.h
, mille lisamisel lisatakse nii tipu, sirglõigu kui kera klasside päised. Meenutan, et dokumenteerimine on endiselt kohustuslik!
Ülesanne 2 – universaalse hulknurga klass (2 punkti)
Tulemusena on vaja luua klassimall Polygon
, mille parameetriteks on punkti klass T
ja täisarv n
(template <class T, unsigned short n>
). Klass esindab n-tipulisi hulknurki üle etteantud punktide. Lisage klassimallile meetodid:
Meetod | Eesmärk |
---|---|
Polygon<T, n> () | algväärtustab tipud nullidega |
Polygon<T, n> (vector<T> pts) | väärtustab vektori koordinaadid etteantud väärtustega |
float perimeter () | annab hulknurga perimeetri (külgede pikkuste summa) |
string toString () | tagastab hulknurka esitava sõne ((tipp1),...,(tipp2)) |
operator << | väljastab hulknurga andmed (kasuta toString meetodit) |
Vihjeid
Abiks väljundvoogu nihutamise operaatori ülelaadimisel (vaata esmalt teise praktikumi materjalist „Lisalugemist väljundvoogu suunamise operaatori ülelaadimise kohta“).
Kui funktsiooni parameeter on konstant, siis andes selle parameetri edasi mõnele teisele funktsioonile peab ta jääma konstantseks. Näiteks, kui klass on kirjeldatud järgmiselt
class Vector2 { ... string toString(); friend ostream& operator<<(ostream& out, const Vector2& vec); }
Siis ei saa operaatori ülelaadimist realiseerida nii
ostream& operator<<(ostream& out, const Vector2& vec) { out << vec.toString(); // veateade return out; }
Probleem seisneb selles, et kompilaator ei tea, mis toString meetodis tehakse ja kas see meetod
muudab Vector2
klassi objekti. Lahenduseks tuleks toString
meetodi lõppu kirjutada const
(string toString() const;
), mis tagab, et objekti seal meetodis ei muudeta ning võimaldab toString
ile teha väljakutseid konstantsete muutujate pealt.
Lisaülesanne 3 – funktsiooniobjektid (kuni 3 punkti)
Üldised nõuded
Selle ülesande lahendamise käigus saate harjutada algoritmide ja funktsiooniobjektidega töötamist.
Tulemusena tekkiv kood peaks sisalduma teegifailis nimega libmyfunctors.a
ja vastav päis olgu myfunctors.h
. Lahenduse testimiseks kirjutan ma programmi, kus rakendan teie kirjutatud koodi
ning kontrollin tulemuste vastavust ülesande nõuetele. Soovitan teil teha testprogramm, kus oma
lahendust ise järele proovite. Testprogrammi ei hinnata.
Lahendus paigutage samasse kataloogipuusse esimese ülesande geomeetriakoodiga. Makefile peab vaikimisi ehitama valmis teegi.
Osa 3.1 – ühe parameetriga ja olekuta funktsiooniobjekt täisruutude äratundmiseks
Kirjutada ühe unsigned long
tüüpi parameetriga predikaatfunktor IsSquare
, mis tagastab tõese väärtuse, kui etteantud täisarv on mingi täisarvu ruut (näited sobivatest arvudest on 0, 1, 4, 9, 16,...).
Funktor peab sobima kasutamiseks algoritmides find_if
, count_if
, replace_if
, remove_if
, remove_copy_if
. Funktsiooniobjekti võiks realiseerida CPP-failis.
Osa 3.2 – ühe parameetriga olekuga funktsiooniobjekt konteineri elementide summeerimiseks
Kiire sissejuhatus olekuga funktorite teemasse. Funktorid võivad olla defineeritud ka enne nende rakendamist. Sellega kaasneb meeldiv lisavõimalus kasutada neid näiteks väärtuste kogumisel. Funktor luuakse enne algoritmi käivitamist ning pärast töö lõppu loetakse objekti seest tulemus. Selle toetamiseks peavad funktori klassis olema lisaks tehtele ()
defineeritud veel vajalikud muutujad ning meetodid. Teie ülesandeks on kirjutada klassiparameetriga T
klassimall SumElements<T>
, mida saaks kasutada T
tüüpi elemente sisaldavate konteinerite sisu summeerimiseks. Eeldame, et tüübil T
on defineeritud tehe +
(sobivad näiteks int
, float
, string
). Kui koodis proovitakse objekti luua mõne tüübiga, millel tehet pole, tekib viga.
Funktsiooniobjekti võiks realiseerida päises. Näide sellest, kuidas objekti peaks saama kasutada.
vector<unsigned long> values; // vektor täisarvudest SumElements<unsigned long> addThisValue; // sama tüüpi funktor addThisValue = for_each(values.begin(), values.end(), addThisValue); // summeeri unsigned long sum = addThisValue.result (); // loeme kogunenud summa
Lahendus peab töötama nii arvuliste tüüpide kui ka sõnede korral. Vihje: Loogeliste sulgude kasutamine objekti loomisel algväärtustab arvulised tüübid nulliks ja sõned tühisõnedeks. Näiteks
int x; // x'i väärtuseks võib olla ükskõik mis int x{}; // x algväärtustatakse nulliks int x(); // Kompilaator arvab, et tegemist on funktsiooni deklaratsiooniga
Lisainfo:
http://en.cppreference.com/w/cpp/language/value_initialization
http://en.cppreference.com/w/cpp/language/default_initialization
Osa 3.3 – näiliselt juhuslikud reaalarvud
Kirjutada funktsiooniobjekt PseudoRandomGenerator<seed>
, mis tagastab juhuslikult genereeritud float
tüüpi arvu. Funktsiooniobjekt peab sobima kasutamiseks algoritmide generate
ja generate_n
abil juhuslikke reaalarve sisaldavate konteinerite (näiteks vektorite) loomiseks. Oluline on, et sama seed
väärtuse korral tekkivad erinevate juhuarvude jadad oleksid võrdsed. Erineva seed
väärtuse korral tekkivad jadad peavad olema erinevad. Vaata lisaks geometrytest.cpp
faili. Juhuslike arvude loomiseks võid kasutada teeki <random>
(C++11).