Institute of Computer Science
  1. Courses
  2. 2022/23 spring
  3. Programming in C++ (LTAT.03.025)
ET
Log in

Programming in C++ 2022/23 spring

  • Pealeht
  • 1. Muutujad ja andmetüübid
  • 2. Keele põhikonstruktsioonid I
  • 3. Keele põhikonstruktsioonid II
  • 4. Funktsioonimallid, failitöötlus
  • 5. OOP I Klassid
  • 6. OOP II Pärilus ja polümorfism
  • 7. Kontrolltöö 1?

Seitsmendal nädalal toimub 1. kontrolltöö

7.1 1. kontrolltöö näide?
  • 9. Dünaamiline mäluhaldus II
  • 10. Klassimallid
  • 11. STL andmestruktuurid I
  • 12. STL andmestruktuurid II
  • 13. Erindite töötlemine
  • 14. Täiendavad teemad
  • 15. Kontrolltöö 2?

Viieteistkümnendal nädalal toimub 2. kontrolltöö

15.1 2. kontrolltöö näide?
  • 16. Projekti esitlus?
  • Viiteid
  • Vanad materjalid
  • Praktikumid
  • Juhendid
  • Viited

Keele põhikonstruktsioonid I

Pärast selle praktikumi läbimist üliõpilane

  • teab ja oskab kasutada tingimuslauseid, sh kolmepoolset tingimuslauset
  • teab korduslausete erinevaid võimalusi C++ programmi koostamisel
  • oskab kasutada C++ massiive
  • oskab genereerida juhuarve

Tingimuslause

Avaldiste võrdlemiseks kasutatakse C++ samu operaatoreid, mis paljudes teistes keeltes: <, <=, >, >=, ==, !=. Loogilised operaatorid on järgmised:

OperaatorTähendusNäide
!Eitus!a
&&ANDa&&b
||ORa||b

Avaldisi && ja || väärtustatakse vasakult paremale. Paremal pool asuvat operandi väärtustatakse ainult siis, kui vasaku poole väärtus ei määra tulemust üheselt. Näiteks koodilõigu

int a{1};
int b{2};
cout << boolalpha << (a == b && b > a) << "\n";
false

tulemuseks on false. Kuna avaldise vasaku poole tõeväärtus on false ja tulemuseks on sõltumata paremast poolest false, siis operaatorist && paremal pool olevat avaldist b > a ei väärtustata. Märgime siin, et ümarsulud väljundvoos cout on avaldise väärtustamiseks vajalikud.

if-lause põhikuju on järgmine:

if (tingimus)
{
//käsud1
}
else 
{
//käsud2
}

Tingimus (mis võib olla keeruline loogiline avaldis) peab olema ümarsulgudes ja else osa võib puududa. Kui lausete plokk koosneb ühest käsust, siis võib loogelised sulud ära jätta. Samuti võivad if laused olla üksteise sees.

int aastaaeg;
cout << "Sisesta aastaaeg (1-4):";
cin >> aastaaeg;
if (aastaaeg == 1){
    cout << "Kevad!\n";
}
else if (aastaaeg == 2){
    cout << "Suvi!\n";
}
else if (aastaaeg == 3){
    cout << "Sügis!\n";
}
else if (aastaaeg == 4){
    cout << "Talv!\n";
}
else{
    cout << "Vale aastaaeg!\n";
}

Sisesta aastaaeg (1-4):2
Suvi!
----
Sisesta aastaaeg (1-4):8
Vale aastaaeg!

Tegelikult võib tingimuslause olla palju keerulisem, näiteks võib kasutada initsialiseerimist, vt https://en.cppreference.com/w/cpp/language/if.

Tingimusoperaator

Tingimusoperaator võimaldab lühendada teatud kujul if-else lauseid. Olgu meil tingimuslause (muutujad a, b, suurim on varem defineeritud).

if (a > b){
suurim = a;
}
else{
suurim = b;
}

Tingimusoperaatoriga saab sama koodi kirja panna ühel real:

suurim = (a > b) ? a : b;

Avaldist omistamisoperaatorist = paremal nimetatakse tingimusoperaatori avaldiseks. Kui tingimus enne ? on tõene (true), siis tagastatakse esimene kahest avaldisest (antud näites a), vastasel juhul tagastatakse teine avaldis (antud näites b).

Kolmepoolne võrdlemine <=> (three-way comparison)

Alates C++20 saab kasutada kolmepoolse võrdlemise operaatorit <=>, mida nimetatakse ka "kosmoselaeva" operaatoriks, sest kellelegi meenutas selle operaatori kuju lendavat taldrikut. Täpsemalt, hüüdnime võttis kasutusele programmeerimiskeele Perl ekspert Randal L. Schwartz, kuna kujund meenutas talle kosmoselaeva 1970ndate tekstipõhises arvutimängus "Star Trek".

Operaatoris on koos kolm võrdlemist (<, ==, >). Avaldis a<=>b määrab, kas a on väiksem, võrdne või suurem kui b:

(a <=> b) < 0 kui a < b,
(a <=> b) > 0 kui a > b,
(a <=> b) == 0 kui a == b (a ja b on võrdsed).

Võrdlemise tulemuse saab salvestada muutujasse, mida saab hiljem nulliga võrrelda. Vaatame näidet

double a{3.2};
double b{2.3};
auto tulemus = a <=> b;
if (tulemus < 0)
    std::cout << "a < b";
else if (tulemus > 0)
    std::cout << "a > b";
else if (tulemus == 0)
    std::cout << "a == b";
else
    std::cout << "a ja b ei ole võrreldavad";
a > b

Kolmepoolse võrdlemisega saab võrrelda ka keerulisemaid operande, nendega saab tutvuda aadressil https://en.cppreference.com/w/cpp/language/operator_comparison.

Korduslaused, break, continue

for-tsükkel

for-tsükli üldkuju on järgmine:

for (eeltegevused; jätkamistingimus; sammu järeltegevused) {
    // käsud, mida tuleb täita niikaua, kuni jätkamistingimus kehtib
}
 

Järgmises näites kasutatakse tsüklimuutujat, mille tüüp on unsigned int:

for(unsigned int i{0} ; i < 3 ; ++i){
    cout << i <<  " : Mulle meeldib C++!\n";
}
0 : Mulle meeldib C++!
1 : Mulle meeldib C++!
2 : Mulle meeldib C++!

Tavaliselt (nt massiivi läbimisel) kasutatakse tsüklimuutujat kasutades size_t (alates C++11). size_t ei ole andmetüüp, ta on standardteegis defineeritud aliasena märgita täisarvutüübile.

for(size_t i{0} ; i < 3 ; ++i){
    cout << i <<  " : Mulle meeldib C++!\n";
}
0 : Mulle meeldib C++!
1 : Mulle meeldib C++!
2 : Mulle meeldib C++!

Tsüklimuutuja for-tsüklis ei pea tingimata olema täisarvutüüpi. Järgmises näites arvutatakse ringi pindala raadiuse eri väärtuste korral ja tsükli täitmist juhitakse ujukomaarvuga.

double pi{3.14159265};
for (double raadius{2.0}; raadius <= 12.0; raadius += 2.5){
    cout <<  raadius << " " << pi * raadius * raadius << "\n";
}
2 12.5664
4.5 63.6173
7 153.938
9.5 283.529
12 452.389

Väljundi formaatimine

Eelmise näite väljund on raskesti loetav. C++ väljundit on võimalik käskudega juhtida. Vaatame näidet, kus on kasutatud teeki <iomanip> võimalusi väljundi formaatimiseks. Selleks tuleb teek <iomanip> programmi kaasata. Selguse mõttes esitame terve programmi.

#include <iostream>
#include <iomanip>

using namespace std;

int main(){
    double pi{3.14159265};
    cout << setw(7) <<  "Raadius"  << setw(10) << "Pindala\n";
    cout << "-----------------\n";
    cout << right;
    for (double raadius{2.0}; raadius <= 12.0; raadius += 2.5){
        cout << setw(7) <<  raadius << setw(10) << pi * raadius * raadius << "\n";
    }
    return 0;
}
Raadius   Pindala
-----------------
      2   12.5664
    4.5   63.6173
      7   153.938
    9.5   283.529
     12   452.389

Käsk cout << setw(7) annab väljundile korralduse, et järgnev info on vaja paigutada väljale laiusega 7 märki. Käsk cout << right annab korralduse, et info tuleb paigutada joondatult paremale (vaikimisi on joondus vasakule). Teek <iomanip> pakub mitmeid teisi võimalusi väljundi formaatimiseks, mida saab uurida siit: https://en.cppreference.com/w/cpp/header/iomanip.

for-tsüklil on mitmeid teisi vorme, nendest tuleb juttu hiljem.

while-tsükkel

while-tsükli üldkuju on sarnane nagu paljudes teistes programmeerimiskeeltes

while (tingimus){
// käsud, mida tuleb täita niikaua, kuni tingimus kehtib
}

Oluline on, et tingimus oleks ümarsulgudes ja tsükli kehas peab hoolitsema selle eest, ei tekiks lõpmatu tsükkel. Kirjutame eelmise programmi ümber kasutades while-tsüklit (programmi algus ja lõpp on ära jäetud):

cout << setw(7) << "Raadius" << setw(10) << "Pindala\n";
cout << "-----------------\n";
cout << right;
double pi{3.14159265};
double raadius{2.0};
while (raadius <= 12.0) {
    cout << setw(7) << raadius << setw(10) << pi * raadius * raadius << "\n";
    raadius += 2.5;
}
Raadius   Pindala
-----------------
      2   12.5664
    4.5   63.6173
      7   153.938
    9.5   283.529
     12   452.389

do while-tsükkel

do while-tsükli üldkuju on järgmine:

do{
// käsud, mida tuleb täita niikaua, kuni tingimus kehtib
} while (tingimus);
 

do while-tsükli keha täidetakse alati vähemalt üks kord. Ringi pindala arvutamise programm kasutades do while-tsüklit:

cout << setw(7) << "Raadius" << setw(10) << "Pindala\n";
cout << "-----------------\n";
cout << right;
double pi{3.14159265};
double raadius{2.0};
do{
    cout << setw(7) << raadius << setw(10) << pi * raadius * raadius << "\n";
    raadius += 2.5;
}while (raadius <= 12.0);
Raadius   Pindala
-----------------
      2   12.5664
    4.5   63.6173
      7   153.938
    9.5   283.529
     12   452.389

Tsüklidirektiivid break ja continue

Tsüklidirektiivi break kasutatakse tsüklist väljumiseks. Tüüpiline kasutus on näiteks lõpmatute tsüklite

while (true){
...
}

või

for (; ; ;){
...
}

töö lõpetamiseks, aga ka teistel juhtudel. Näiteks lõpmatu tsükkel kasutajalt korrektse sisendi küsimiseks:

int arv;
while(true){
    cout << "Sisesta arv ühest kümneni:";
    cin >> arv;
    if (arv >= 1 && arv <= 10){
        cout << "Sisestasid arvu " << arv;
        break;
    }
}
Sisesta arv ühest kümneni:15
Sisesta arv ühest kümneni:5
Sisestasid arvu 5

Tsüklidirektiiv continue tsükli sees lõpetab käsil oleva tsüklisammu ja täitmisele tuleb järgmine tsüklisamm.

char ch{};
cout << left << setw(10) << "Märk" << setw(10) << "Kood\n";
do{
    if (!isprint(ch)){
        continue;
    }
    cout << left << setw(10) << ch << setw(10) << static_cast<int>(ch) << "\n";
}while (ch++ < 127);
Märk     Kood
          32
!         33
"         34
#         35
$         36
%         37
&         38
'         39

Programm kuvab ekraanile ASCII kooditabelist prinditavad märgid, mille ASCII kood on väiksem kui 127. Standardnimeruumi funktsioon isprint tagastab tõeväärtuse true, kui koodile vastab prinditav sümbol. Väljundist on toodud vaid osa. Märgime siin, et ASCII koode 0 kuni 31 kasutatakse juhtsümbolitena ja nendel prinditav kuju puudub. Koodile 32 vastab tühik.

Käsk switch - lüliti

switch lause põhikuju on järgmine:

switch (avaldis) {
case väärtus1:
//käsud, mis täidetakse, kui avaldis = väärtus1
break;
case väärtus2:
//käsud, mis täidetakse, kui avaldis = väärtus2
break;
...
default:
//käsud, mis täidetakse, kui ükski eelnev ei sobinud
}

Avaldise tüüp peab olema teisendatav kas char või täisarvutüübiks. Aastaaegade näite saab switch abil kirja panna järgmiselt:

switch (aastaaeg) {
    case 1:
        cout << "Kevad!\n";
        break;
    case 2:
        cout << "Sisestasid: " << aastaaeg << "\n";
        cout << "Suvi!\n";
        break;
    case 3:
        cout << "Sügis!\n";
        break;
    case 4:
        cout << "Talv!\n";
        break;
    default:
        cout << "Vale aastaaeg!\n";
}
Sisesta aastaaeg (1-4):2
Sisestasid: 2
Suvi!

Paneme tähele, et siin ei pea plokis mitme käsu jaoks loogelisi sulge kasutama (aga võib). Käsk break lõpetab siin switch täitmise. Käsu break võib jätta ära (näites on alates case 2: käsud break välja kommenteeritud), kuid sellel on kõrvalefekt:

switch (aastaaeg) {
    case 1:
        cout << "Kevad!\n";
        break;
    case 2:
        cout << "Sisestasid: " << aastaaeg << "\n";
        cout << "Suvi!\n";
        //break;
    case 3:
        cout << "Sügis!\n";
        //break;
    case 4:
        cout << "Talv!\n";
        //break;
    default:
        cout << "Vale aastaaeg!\n";
}
Sisesta aastaaeg (1-4):2
Sisestasid: 2
Suvi!
Sügis!
Talv!
Vale aastaaeg!

Mitu case haru on lubatud? C++ standard soovitab toetada vähemalt 16384 case haru! Ka switch lausel on mitmeid eri võimalusi, lähemalt saab uurida siit https://en.cppreference.com/w/cpp/language/switch

Massiivid

Ühedimensionaalset massiivi saab defineerida järgmiselt:

tüüp muutuja_nimi[elementide_arv];

Massiivi definitsioonis on nurksulud alati muutuja nime järel. Näites toodud lause ei väärtusta massiivi elemente. Juba defineeritud massiiv on konstantse pikkusega, st elementide arvu ei saa muuta ja indeksid algavad nullist. Massiivi elementide läbivaatuseks on sobiv kasutada tsüklit.

int kaheastmed[5];
for (int i{0}; i < 5; ++i) {
    kaheastmed[i] = exp2(i);
}
cout << "Kaheastmed: \n";
for (int i{0}; i < 5; ++i) {
    cout << i << " " << kaheastmed[i] << "\n";
}
Kaheastmed:
0 1
1 2
2 4
3 8
4 16

Näites on kasutatud funktsiooni exp2, mis arvutab argumendi kahe astme. Funktsioon asub teegis <cmath>, st programmi alguses peab olema käsk #include <cmath>. Vaatame nüüd ujukomaarvude massiivi, mille elemente ei algväärtustata ja elementide väljastamisel minnakse indeksiga "üle otsa". Massiivi deklareerimisel määratakse massiivi muutujale mälupiirkonna algus. Ekraanile kuvamisel püütakse mälus olevat infot esitada double tüüpi arvudena.

double d[3];
for (int i{0}; i < 5; ++i) {
    cout << i << " " << d[i] << "\n";
}
0 7.3549e-312
1 2.122e-314
2 6.95148e-310
3 6.36599e-314
4 3.30536e-313

Massiivi elemente saab defineerimisel väärtustada, siis võib nurksulgudes elementide arvu ära jätta.

int massiiv[]{3, 8, 2};

Kui nurksulgudes on suurem arv kui on defineeritud elemente, siis ülejäänutele omistatakse null.

int massiiv[5]{3, 8, 2};
for (int i{0}; i < 5; ++i) {
    cout << i << " " << massiiv[i] << "\n";
}
0 3
1 8
2 2
3 0
4 0

Näites massiivi elemendid indeksitega 3 ja 4 on nullid. Seni toodud näidetes on kasutatud massiivi indeksit. Kui massiivi indeksit ei vajata, siis on võimalik kasutada nn forEach tsüklit (range-based for loop), kus pole vaja muretseda elementide arvu pärast.

int massiiv[]{3, 8, 2};
for (int arv : massiiv) {
    cout << arv << "\n";
}
3
8
2

NB! C++ ei kontrolli massiivi indekseid, seda peab tegema programmeerija ise. Näiteks on võimalik eelmise massiivi korral omistada

massiiv[5] = 12;

kusjuures viga ei teki nii kompileerimisel kui ka täitmisel. Selline teguviis võib viia programmi kokkujooksmiseni.

Massiivi elementide arvu määramiseks kasutatakse sageli funktsiooni sizeof, mis tagastab argumendi suuruse baitises (massiivi korral massiivi all oleva mälupiirkonna suuruse, tüübi korral tüübi suuruse baitides).

int massiiv[] {3, 8, 2};
size_t massiivi_elementide_arv = sizeof(massiiv)/sizeof(int);
cout << massiivi_elementide_arv << "\n";
3

Palju mugavam on kasutada teegis <array> (algselt teegis <iterator>) defineeritud standardnimeruumi funktsiooni size (alates C++17).

Mitmedimensionaalseid massiive defineeritakse analoogiliselt, kusjuures kõige vasakpoolsema dimensiooni võib ära jätta.

int tabel[][3][2] = {{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}};
for (size_t i{0}; i < size(tabel); ++i) {
  for (int j{0}; j < size(tabel[0]); ++j) {
    for (int k{0}; k < size(tabel[0][0]); ++k) {
      cout << tabel[i][j][k] << " ";
    }
    cout << "\n";
  }
  cout << "---\n";
}
1 2
3 4
5 6
---
7 8
9 10
11 12
---

Massiiv char tüüpi elementidest

Massiiv char-tüüpi elementidest võib olla kahte tüüpi. Massiiv võib olla lihtsalt märkide kogum

char sõnum[]{'T', 'e', 'r', 'e'};

või märkide kogum, mille viimaseks elemendiks on nn nullmärk (null character)

char sõnum[]{'T', 'e', 'r', 'e', '\0'};

Viimati defineeritud massiiv võib esindada ka sõnet (string). Nullmärk on sõne lõpu tunnus. Märgimassiivi, mille lõpus on '\0', nimetatakse C-stiilis sõneks (string). Standardnimeruumis on olemas ka liittüüp string, mida käsitletakse hiljem. Sõne tüüpi char massiivi saab defineerida ka järgmiselt:

char nimi[]{"Tuule Lohe"};

Tulemuseks luuakse C-stiilis sõne (nullmärk lisatakse siin automaatselt). Sellist massiivi saab otse ekraanile kuvada:

char nimi[]{"Tuule Lohe"};
cout << nimi << "\n";
Tuule Lohe

NB! Selliselt ei saa ekraanile kuvada teisi massiive (arvumassiive või char massiive ilma nullmärgita lõpus). Näiteks, kui üritada ekraanile tuua täisarvumassiivi, siis tulemus võib olla midagi taolist:

int arvud[]{1, 2, 3};
cout << arvud << "\n";
0x6bf1ff5e4

char massiivi läbivaatamist saab toimetada for-tsükliga. Järgmises näites on vajalik päise #include <cctype> olemasolu. Lõputunnusena kontrollitakse for-tsüklis nullmärgi ('\0') olemasolu, aga võib ka i < size(massiiv).

const int max_pikkus{20}; // Massiivi suurus (peab defineerima konstandina)
char massiiv[max_pikkus]{}; // Massiiv, kuhu lugeda
cout << "Sisesta tekst:\n";
cin.getline(massiiv, max_pikkus); //cin meetod, võimalik lugeda tühikuid sisaldavat lauset 
cout << "Sisestasid:\n" << massiiv << "\n";
int tähemärke{};
int numbreid{};
for (int i{}; massiiv[i] != '\0'; i++) {
    if (isalpha(massiiv[i])) {
        tähemärke++;
    }
    if (isdigit(massiiv[i])) {
        numbreid++;
    }
}
cout << "Sisestatud lauses oli " << tähemärke << " tähemärki " << "ja " << numbreid << " numbrit.\n";
Sisesta tekst:
254 Kolm k2
Sisestasid:
254 Kolm k2
Sisestatud lauses oli 5 tähemärki ja 4 numbrit.

Etteantud suurusega char massiivi saab defineerida konstantse pikkusega, seetõttu on vajalik võtmesõna const muutuja max_pikkus ees. Käsk cin.getline(massiiv, max_pikkus) võimaldab klaviatuurilt lugeda lauset (tühikutega eraldatud teksti) ühte muutujasse (massiivi). Teegi <cctype> funktsioonid isalpha ja isdigit teevad kindlaks, kas etteantud märk on vastavalt kas tähemärk või number. Teiste funktsioonidega teegist <cctype> saab tutvuda siin https://en.cppreference.com/w/cpp/header/cctype.

Eriti mugav on defineerida kahedimensionaalseid C-stiilis sõnemassiive ja neid töödelda ühekordse tsükliga:

char kuulsused[][30]{
    "Erki Nool ",
    "Ott Lepland",
    "Birgit Sarrap",
    "Tõnis Niinemets",
};
for (int i{0}; i < size(kuulsused); ++i) {
    cout << kuulsused[i] << "\n";
 }
Erki Nool
Ott Lepland
Birgit Sarrap
Tõnis Niinemets

vector - dünaamiline massiiv

Standardmallide teek STL (Standard Template Library) sisaldab teeki std::<vector>, mis võimaldab luua dünaamilist (muutuva suurusega) massiivi. Vektorisse võib elemente lisada, elemente kustutada või muuta. Selleks tuleb programmi algul kaasata teek <vector>.

#include <vector>

Vektorisse saab salvestada ainult sama tüüpi elemente, mille tüüp tuleb vektori loomisel näidata:

vector<int> m; // tühi vektor
vector<double> arvud(10); // sisaldab 10 elementi, kõik nullid
vector<string> sõned(5); // 5 elementi, kõik tühisõned

Loogeliste sulgudega initsialiseeerimisel saab ette anda elementide loetelu:

vector<int> m1{5}; // üks element: 5
vector<double> arvud{1.1, 2.2}; // 2 elementi: 1.1 ja 2.2

Elemente saab kätte indeksi abil, elementide arvu annab funktsioon size. Läbivaatuseks saab kasutada nii for kui ka forEach tsüklit:

#include <iostream>
#include <vector>
using namespace std;
int main() {
    vector<double> arvud{1.1, 2.2}; // 2 elementi: 1.1 ja 2.2
    arvud.push_back(3.3); // lisame elemendi 3.3
    cout << "Esialgne:\n";
    for (size_t i = 0; i < arvud.size(); ++i) {
        cout << arvud[i] << " ";
    }
    for (size_t i = 0; i < arvud.size(); ++i) {
        arvud[i] += 1; // suurendame ühe võrra
    }
    cout << "\nÜhe võrra suurendatult:\n";
    for (size_t i = 0; i < arvud.size(); ++i) {
        cout << arvud[i] << " ";
    }
    erase(arvud, 1.1); // kustutame 1.1 esinemised
    cout << "\n1.1 on kustutatud:\n";
    for (double d: arvud) {
        cout << d << " ";
    }

    return 0;
}
Esialgne:
1.1 2.2 3.3
Ühe võrra suurendatult:
2.1 3.2 4.3
1.1 on kustutatud:
2.1 3.2 4.3

Lähemalt saab <vector> võimalusi uurida aadressilt https://en.cppreference.com/w/cpp/container/vector

NB! Dokumendis https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines.html#Rsl-arrays soovitatakse eelistada <vector> tüüpi dünaamilisi massiive fikseeritud pikkusega massiividele.

Juhuarvude genereerimine

Esmalt tutvume C-keelest pärit juhuslike täisarvude genereerimisega. Teegis <cstdlib> on funktsioon rand, mis genereerib juhusliku täisarvu vahemikus 0..RAND_MAX. Genereerime kuus täisarvu vahemikus 0..RAND_MAX ja kuus täisarvu vahemikus 5 .. 15, kus otspunktid on kaasa arvatud. Viimasel juhul sobiva tulemuse saamiseks kasutame jagamise jääki ja nihutamist rand()%11 + 5. Mõned näited juhuslike täisarvude arvutamisest vahemiku järgi

arv1 = rand() % 100;         // arv1 vahemikus 0..99
arv2 = rand() % 100 + 1;     // arv2 vahemikus 1..100
arv3 = rand() % 40 + 1985;   // arv3 vahemikus 1985..2024 

Tulemus võiks olla näiteks selline:

#include <iostream>
#include <cstdlib>
using namespace std;

int main() {
    cout << "Juhuarvud vahemikus 0 .. RAND_MAX:" << "\n";
    for (int i = 0; i < 6; ++i) {
        cout << rand() << " ";
    }
    cout << "\nJuhuarvud vahemikus 5 .. 15:" << "\n";
    for (int i = 0; i < 6; ++i) {
        cout << rand()%11 + 5 << " ";
    }
    return 0;
}
Juhuarvud vahemikus 0 .. RAND_MAX:
41 18467 6334 26500 19169 15724
Juhuarvud vahemikus 5 .. 15:
10 15 6 5 12 12

Kui seda programmi käivitada mitu korda, siis iga kord saame samad arvud. Selle vältimiseks kasutatakse funktsiooni srand, mille abil saab genereerijale ette anda nn "seemne", et funktsioon tagastaks igal käivitamisel uued juhuarvud. Seemneks sobib funktsiooni time (teegist <ctime>) tagastatud hetkeaeg, mis on igal käivitusel erinev.

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
int main() {
    srand(time(0));
    cout << "Juhuarvud vahemikus 0 .. RAND_MAX:" << "\n";
    for (int i = 0; i < 6; ++i) {
        cout << rand() << " ";
    }
    cout << "\nJuhuarvud vahemikus 5 .. 15:" << "\n";
    for (int i = 0; i < 6; ++i) {
        cout << rand()%11 + 5 << " ";
    }
    return 0;
}

Esimene kord käivitades:

Juhuarvud vahemikus 0 .. RAND_MAX:
41 18467 6334 26500 19169 15724
Juhuarvud vahemikus 5 .. 15:
10 15 6 5 12 12

Teine kord käivitades:

Juhuarvud vahemikus 0 .. RAND_MAX:
16987 22816 1300 21150 20282 23915
Juhuarvud vahemikus 5 .. 15:
10 10 9 14 13 15

NB! Juhuslike arvude genereerimisel rand abil on mitmeid puudusi, seetõttu ei soovitata seda lähenemist reaalsetes projektides kasutada, vt https://en.cppreference.com/w/c/numeric/random/rand

Alates C++11-st on kasutusel teek <random>, millel on palju rohkem võimalusi: mitmed juhuslike arvude generaatorid, erinevad juhuslike arvude jaotused jne. Toome siin väikese näite:

#include <iostream>
#include <random>
using namespace std;
int main() {
    default_random_engine genereerija;
    uniform_int_distribution<int> jaotus(5,15);
    cout << "Teegi <random> abil genereeritud täisarvud vahemikus 5..15 (ühtlase jaotusega)\n";
    for (size_t i = 0; i < 10; ++i) {
        cout << jaotus(genereerija) << " ";
    }
    return 0;
}
Teegi <random> abil genereeritud täisarvud vahemikus 5..15 (ühtlase jaotusega)
5 6 13 10 10 7 5 12 12 15

Täpsemalt saab <random> võimalusi uurida aadressil https://en.cppreference.com/w/cpp/numeric/random

NB! Enesetestides eeldame, et on kasutatud standardnimeruumi (using namespace std;)

<< Näita enesetesti >>

<< Näita enesetesti >>

<< Näita enesetesti >>

<< Näita enesetesti >>

<< Näita enesetesti >>


  • Institute of Computer Science
  • Faculty of Science and Technology
  • University of Tartu
In case of technical problems or questions write to:

Contact the course organizers with the organizational and course content questions.
The proprietary copyrights of educational materials belong to the University of Tartu. The use of educational materials is permitted for the purposes and under the conditions provided for in the copyright law for the free use of a work. When using educational materials, the user is obligated to give credit to the author of the educational materials.
The use of educational materials for other purposes is allowed only with the prior written consent of the University of Tartu.
Terms of use for the Courses environment