From f8796d557a2c4284298a3239db3c04d461b5154d Mon Sep 17 00:00:00 2001 From: null68 Date: Sat, 4 Jul 2026 04:04:20 +0200 Subject: [PATCH 1/6] feat: possible exam solution --- .../PR2_2026-06-25_G1_Rje\305\241enje.cpp" | 628 ++++++++++++++++++ .../2026-06-25/PR_2026-06-25_G1_Postavka.cpp | 288 ++++++++ Ispiti/2026-06-25/README.md | 12 + 3 files changed, 928 insertions(+) create mode 100644 "Ispiti/2026-06-25/PR2_2026-06-25_G1_Rje\305\241enje.cpp" create mode 100644 Ispiti/2026-06-25/PR_2026-06-25_G1_Postavka.cpp create mode 100644 Ispiti/2026-06-25/README.md diff --git "a/Ispiti/2026-06-25/PR2_2026-06-25_G1_Rje\305\241enje.cpp" "b/Ispiti/2026-06-25/PR2_2026-06-25_G1_Rje\305\241enje.cpp" new file mode 100644 index 0000000..5ff0bea --- /dev/null +++ "b/Ispiti/2026-06-25/PR2_2026-06-25_G1_Rje\305\241enje.cpp" @@ -0,0 +1,628 @@ +#include +#include +#include +#include + +// za ostringstream +#include + +// za setfill i setw funkcije +#include + +// za ifstream +#include + +using namespace std; + + +string crt = "\n------------------------------------------------------ -------------------------- - \n"; + +string PORUKA_TELEFON = crt + +"TELEFONE ISKLJUCITE I ODLOZITE U TORBU, DZEP ILI DRUGU LOKACIJU VAN DOHVATA.\n" +"CESTO SE NA TELEFONIMA (PRO)NALAZE PROGRAMSKI KODOVI KOJI MOGU BITI ISKORISTENI ZA\n" +"RJESAVANJE ISPITNOG ZADATKA, STO CE, U SLUCAJU PRONALASKA, BITI SANKCIONISANO." + crt; + +string PORUKA_ISPIT = crt + +"0. PROVJERITE DA LI ZADACI PRIPADAJU VASOJ GRUPI (G1/G2)\n" +"1. SVE KLASE SA DINAMICKOM ALOKACIJOM MORAJU IMATI ISPRAVAN DESTRUKTOR\n" +"2. IZOSTAVLJANJE DESTRUKTORA ILI NJEGOVIH DIJELOVA BIT CE OZNACENO KAO TM\n" +"3. ATRIBUTI, METODE I PARAMETRI MORAJU BITI IDENTICNI ONIMA U TESTNOJ MAIN FUNKCIJI, \n" +" OSIM AKO POSTOJI JASNO OPISAN RAZLOG ZA MODIFIKACIJU\n" +"4. IZUZETKE BACAJTE SAMO TAMO GDJE JE IZRICITO NAGLASENO\n" +"5. SVE METODE KOJE SE POZIVAJU U MAIN-U MORAJU POSTOJATI.\n" +" AKO NEMATE ZELJENU IMPLEMENTACIJU, OSTAVITE PRAZNO TIJELO ILI VRATITE DEFAULT VRIJEDNOST\n" +"6. U MAIN FUNKCIJI MOZETE DODAVATI TESTNE PODATKE I POZIVE PO VLASTITOM IZBORU\n" +"7. TESTIRAJTE PROGRAM U OBA MODA (F5 i Ctrl+F5)" + crt; + +char* AlocirajTekst(const char* tekst) { + if (!tekst) return nullptr; + size_t vel = strlen(tekst) + 1; + char* temp = new char[vel]; + strcpy_s(temp, vel, tekst); + return temp; +} + + +string GenerisiSifru(const char* imePrezime, int redniBroj) { + time_t vrijeme = time(NULL); + struct tm vrijeme_info; + localtime_s(&vrijeme_info, &vrijeme); + + ostringstream buffer; + + // pretvaram da dobijem .find() metodu + string s_imePrezime = imePrezime; + + buffer << "CW-" << imePrezime[0] << imePrezime[s_imePrezime.find(" ", 1) + 1] << "-" << setfill('0') << setw(3) << redniBroj << "/" << (vrijeme_info.tm_year + 1900); + + return buffer.str(); +} + +bool ValidirajSifru(const string& sifra) { + return regex_match(sifra, regex("CW-([A-Z]{2})-([0-9]{3})/([0-9]{4})")); +} + +enum TipProstora { RADNO_MJESTO, SALA, STUDIO, LABORATORIJA }; + +const char* TipProstoraNazivi[] = { "RADNO MJESTO", "SALA", "STUDIO", "LABORATORIJA" }; + +const char* GetTipProstoraCStr(TipProstora prostor) { + return TipProstoraNazivi[prostor]; +} + + +template +class Kolekcija { + T1 _prvi[max]; + T2 _drugi[max]; + int* _trenutno; +public: + Kolekcija() { _trenutno = new int(0); } + ~Kolekcija() { delete _trenutno; _trenutno = nullptr; } + int GetTrenutno() const { return *_trenutno; } + T1& GetPrvi(int indeks) { return _prvi[indeks]; } + T2& GetDrugi(int indeks) { return _drugi[indeks]; } + T1& operator[](int indeks) { return _prvi[indeks]; } + friend ostream& operator<<(ostream& COUT, Kolekcija& obj) { + for (int i = 0; i < obj.GetTrenutno(); i++) + COUT << obj.GetPrvi(i) << " " << obj.GetDrugi(i) << endl; + return COUT; + } + + // method i added below + + Kolekcija(const Kolekcija& other) { + _trenutno = new int(other.GetTrenutno()); + + for (int i = 0; i < GetTrenutno(); i++) { + _prvi[i] = other._prvi[i]; + _drugi[i] = other._drugi[i]; + } + } + + Kolekcija& operator=(const Kolekcija& other) { + if (this == &other) return *this; + // nema potrebe brisat i ponovo pravit new int nek samo preuzme value (ako sam negdje to uradio zapravo, idk bro 4 ujutro je) + *_trenutno = other.GetTrenutno(); + + for (int i = 0; i < GetTrenutno(); i++) { + _prvi[i] = other._prvi[i]; + _drugi[i] = other._drugi[i]; + } + + return *this; + } + + void Dodaj(T1 el1, T2 el2) { + if (*_trenutno >= max) return; + + _prvi[*_trenutno] = el1; + _drugi[*_trenutno] = el2; + + *_trenutno += 1; + } + + Kolekcija DodajNaPoziciju(int pos, T1 el1, T2 el2) { + Kolekcija nova; + + for (int i = 0; i < GetTrenutno(); i++) { + if (i == pos) { + nova.Dodaj(el1, el2); + } + + nova.Dodaj(_prvi[i], _drugi[i]); + } + *this = nova; + return nova; + } + + Kolekcija* UkloniRaspon(int from, int elements) { + if (from < 0 || elements >= *_trenutno || from + elements >= *_trenutno) { + throw exception("invalid range - UkloniRaspon()"); + } + + Kolekcija* removed = new Kolekcija(); + + for (int i = from; i < from + elements; i++) { + removed->Dodaj(_prvi[i], _drugi[i]); + } + + for (int i = from + elements; i < *_trenutno; i++) { + _prvi[i - elements] = _prvi[i]; + _drugi[i - elements] = _drugi[i]; + } + + *_trenutno -= elements; + + return removed; + } +}; + +class DatumVrijeme { + int* _godina, * _mjesec, * _dan, * _sati, * _minute; +public: + DatumVrijeme(int dan = 1, int mjesec = 1, int godina = 2000, int + sati = 0, int minute = 0) { + _godina = new int(godina); + _mjesec = new int(mjesec); + _dan = new int(dan); + _sati = new int(sati); + _minute = new int(minute); + } + ~DatumVrijeme() { + delete _godina; delete _mjesec; delete _dan; + delete _sati; delete _minute; + } + friend ostream& operator<<(ostream& COUT, DatumVrijeme& obj) { + COUT << *obj._dan << "." << *obj._mjesec << "." << + *obj._godina << " " << *obj._sati << ":" << *obj._minute; + return COUT; + } + + // methods i added below + + + // bez copy constructora i copy assignmenta se pravi shallow copy ovog objekta pa se ovi brojevi sjebu, ugl dodaj u svaku klasu ovo 2 i neces imat problema + DatumVrijeme(const DatumVrijeme& other) { + _godina = new int(other.GetGodina()); + _mjesec = new int(other.GetMjesec()); + _dan = new int(other.GetDan()); + _sati = new int(other.GetSati()); + _minute = new int(other.GetMinute()); + } + + DatumVrijeme& operator=(const DatumVrijeme& other) { + if (this == &other) return *this; + delete _godina; delete _mjesec; delete _dan; + delete _sati; delete _minute; + + _godina = new int(other.GetGodina()); + _mjesec = new int(other.GetMjesec()); + _dan = new int(other.GetDan()); + _sati = new int(other.GetSati()); + _minute = new int(other.GetMinute()); + + return *this; + } + + int GetGodina() const { + return *_godina; + } + + int GetMjesec() const { + return *_mjesec; + } + int GetDan() const { + return *_dan; + } + + int GetSati() const { + return *_sati; + } + + int GetMinute() const { + return *_minute; + } + + bool operator==(const DatumVrijeme& other) const { + return (GetGodina() == other.GetGodina() && GetMjesec() == other.GetMjesec() && GetDan() == other.GetDan() && GetSati() == other.GetSati() && GetMinute() == other.GetMinute()); + } + +}; +class Rezervacija { + char* _oznaka; + TipProstora _tipProstora; + DatumVrijeme _pocetak; + int _trajanjeMinuta; +public: + Rezervacija(const char* oznaka, TipProstora tipProstora, + DatumVrijeme pocetak, int trajanjeMinuta) + : _tipProstora(tipProstora), _pocetak(pocetak), + _trajanjeMinuta(trajanjeMinuta) { + _oznaka = AlocirajTekst(oznaka); + } + ~Rezervacija() { delete[] _oznaka; _oznaka = nullptr; } + const char* GetOznaka() const { return _oznaka; } + TipProstora GetTipProstora() const { return _tipProstora; } + DatumVrijeme& GetPocetak() { return _pocetak; } + int GetTrajanjeMinuta() const { return _trajanjeMinuta; } + + // methods i added below + + Rezervacija(const Rezervacija& other) { + _oznaka = AlocirajTekst(other._oznaka); + _tipProstora = other._tipProstora; + _pocetak = other._pocetak; + _trajanjeMinuta = other._trajanjeMinuta; + } + + Rezervacija& operator=(const Rezervacija& other) { + if (this == &other) return *this; + delete[] _oznaka; _oznaka = nullptr; + + _oznaka = AlocirajTekst(other._oznaka); + _tipProstora = other._tipProstora; + _pocetak = other._pocetak; + _trajanjeMinuta = other._trajanjeMinuta; + + return *this; + } + + + string ToString() { + ostringstream buffer; + + buffer << setfill('0') << setw(2) << _pocetak.GetDan() << "."; + buffer << setfill('0') << setw(2) << _pocetak.GetMjesec() << "."; + buffer << _pocetak.GetGodina() << " "; + buffer << setfill('0') << setw(2) << _pocetak.GetSati() << ":"; + buffer << setfill('0') << setw(2) << _pocetak.GetMinute(); + + + buffer << " " << _oznaka << " " << GetTipProstoraCStr(_tipProstora) << " " << _trajanjeMinuta << " min"; + + return buffer.str(); + } + // ako sam dobro skonto ovo je false samo ako rezervacija A zavrsi prije nego sto rezervacija B pocne i obrnuto + bool ImaKonfliktSa(Rezervacija rezervacijaB) { + if (_pocetak.GetDan() != rezervacijaB._pocetak.GetDan() || + _pocetak.GetMjesec() != rezervacijaB._pocetak.GetMjesec() || + _pocetak.GetGodina() != rezervacijaB._pocetak.GetGodina()) + return false; + + int a_Start = _pocetak.GetSati() * 60 + _pocetak.GetMinute(); + int a_End = a_Start + _trajanjeMinuta; + + int b_Start = rezervacijaB._pocetak.GetSati() * 60 + _pocetak.GetMinute(); + int b_End = b_Start + rezervacijaB._trajanjeMinuta; + + return !(a_End <= b_Start || b_End <= a_Start); + } +}; + +class Korisnik { + static int _id; + char* _sifra; + char* _imePrezime; + vector _rezervacije; +public: + Korisnik(const char* imePrezime = "") { + _imePrezime = AlocirajTekst(imePrezime); + _sifra = AlocirajTekst(GenerisiSifru(imePrezime, + _id).c_str()); + _id++; + } + ~Korisnik() { + delete[] _sifra; _sifra = nullptr; + delete[] _imePrezime; _imePrezime = nullptr; + } + + Korisnik(const Korisnik& other) { + _imePrezime = AlocirajTekst(other._imePrezime); + _sifra = AlocirajTekst(other._sifra); + _rezervacije = other._rezervacije; + } + + Korisnik& operator=(const Korisnik& other) { + if (this == &other) return *this; + delete[] _sifra; _sifra = nullptr; + delete[] _imePrezime; _imePrezime = nullptr; + + _imePrezime = AlocirajTekst(other._imePrezime); + _sifra = AlocirajTekst(other._sifra); + _rezervacije = other._rezervacije; + return *this; + } + + const char* GetSifra() const { return _sifra; } + const char* GetImePrezime() const { return _imePrezime; } + vector& GetRezervacije() { return _rezervacije; } + friend ostream& operator<<(ostream& COUT, Korisnik& obj) { + COUT << obj._imePrezime << " [" << obj._sifra << "]" << endl; + for (auto& rezervacija : obj._rezervacije) + //ToString metoda klase Rezervacija vraca podatke o rezervaciji u formatu : + //10.09.2026 09:00 SALA-A SALA 60 min + COUT << " - " << rezervacija.ToString() << endl; + return COUT; + } + + // methods i added below + + bool DodajRezervaciju(Rezervacija rezervacijaZaDodat) { + + int minute = rezervacijaZaDodat.GetTrajanjeMinuta(); + + for (auto& rezervacija : _rezervacije) { + if (rezervacijaZaDodat.ImaKonfliktSa(rezervacija)) return false; + + // provjera da li je u istom danu + if (rezervacija.GetPocetak().GetDan() == + rezervacijaZaDodat.GetPocetak().GetDan() && + rezervacija.GetPocetak().GetMjesec() == + rezervacijaZaDodat.GetPocetak().GetMjesec() && + rezervacija.GetPocetak().GetGodina() == + rezervacijaZaDodat.GetPocetak().GetGodina()) + { + minute += rezervacija.GetTrajanjeMinuta(); + } + } + if (minute > 480) return false; + + _rezervacije.push_back(rezervacijaZaDodat); + return true; + } + +}; + +class CentarZaRad { + char* _naziv; + vector _korisnici; +public: + CentarZaRad(const char* naziv) { _naziv = AlocirajTekst(naziv); } + ~CentarZaRad() { delete[] _naziv; _naziv = nullptr; } + CentarZaRad(const CentarZaRad& obj) { + _naziv = AlocirajTekst(obj._naziv); + _korisnici = obj._korisnici; + } + const char* GetNaziv() const { return _naziv; } + vector& GetKorisnici() { return _korisnici; } + + void DodajKorisnika(Korisnik korisnik) { + for (auto& k : _korisnici) { + if (strcmp(k.GetSifra(), korisnik.GetSifra()) == 0) { + throw exception("korisnik sa istom sifrom vec postoji"); + } + } + _korisnici.push_back(korisnik); + } + + bool RegistrujRezervaciju(const char* sifra, Rezervacija rezervacija) { + for (auto& korisnik : _korisnici) { + if (strcmp(korisnik.GetSifra(), sifra) == 0) { + korisnik.DodajRezervaciju(rezervacija); + return true; + } + } + + return false; + } + + vector AktivniKorisnici(int min) { + vector aktivniKorisnici; + + for (auto& k : _korisnici) { + if (k.GetRezervacije().size() >= min) { + aktivniKorisnici.push_back(new Korisnik(k)); + } + } + + return aktivniKorisnici; + } + + Kolekcija KoristenjePoTipu(TipProstora prostor) { + Kolekcija korisnici; + + for (auto& k : _korisnici) { + for (auto& r : k.GetRezervacije()) { + if (r.GetTipProstora() == prostor && k.GetRezervacije().size() >= 1) { + korisnici.Dodaj(k, r.GetTrajanjeMinuta()); + } + } + } + + return korisnici; + } +}; + +const char* GetOdgovorNaPrvoPitanje() { + cout << "Pitanje -> Pojasnite razliku izmedju koristenja kljucne rijeci abstract i cistih virtualnih metoda ? \n"; + return "abstract klasa je klasa koja ima bar jednu cistu virtualnu metodu i ne moze se instancirat, cista virtualna metoda je samo deklaracija funkcije bez implementacije u baznoj klasi i prisiljava child klase da se implementiraju"; +} +// ne znam ni sta pita lik xd +const char* GetOdgovorNaDrugoPitanje() { + cout << "Pitanje -> Pojasnite korelaciju izmedju polimorfizma i virtualnih metoda, te zbog cega se javlja potreba za virtualnim destruktorom ? \n"; + return "Polimorfizam omogućava da se preko pokazivača ili reference na baznu klasu pozivaju metode izvedene klase, a to se postiže virtualnim funkcijama koje omogućavaju runtime binding. Virtualni destruktor je potreban da bi se pri brisanju objekta preko baznog pokazivača pravilno pozvao destruktor izvedene klase i spriječilo curenje resursa."; +} +int Korisnik::_id = 1; + + +CentarZaRad* GetCentar(const char* naziv, vector centri) { + for (auto& centar : centri) { + if (strcmp(naziv, centar.GetNaziv()) == 0) return ¢ar; + } + return nullptr; +} + +bool UcitajPodatke(const char* nazivFajla, vector& centri) { + ifstream file(nazivFajla); + + if (!file.is_open()) { + return false; + } + + string s_centar; + string s_korisnik; + bool dodano = false; + while (getline(file, s_centar, '|') && getline(file, s_korisnik)) { + CentarZaRad* centar = GetCentar(s_centar.c_str(), centri); + if (!centar) { + centri.push_back(CentarZaRad(s_centar.c_str())); + centar = ¢ri.back(); + dodano = true; + } + + Korisnik korisnik(s_korisnik.c_str()); + bool duplikat = false; + for (auto& k : centar->GetKorisnici()) { + if (strcmp(k.GetImePrezime(), korisnik.GetImePrezime()) == 0) duplikat = true; + } + if (!duplikat) { + centar->DodajKorisnika(korisnik); + dodano = true; + } + } + return dodano; +} + +int main() { + + cout << PORUKA_TELEFON; cin.get(); + cout << PORUKA_ISPIT; cin.get(); system("cls"); + cout << GetOdgovorNaPrvoPitanje() << crt; + cin.get(); + cout << GetOdgovorNaDrugoPitanje() << crt; + cin.get(); + + //funkcija generise sifru korisnika na osnovu imena i prezimena, rednog broja i trenutne godine. + //sifra je u formatu CW-IN-BBB/GGGG, gdje IN predstavlja inicijale, BBB redni broj korisnika + //popunjen nulama na slobodnim mjestima, a GGGG trenutnu godinu dobijenu iz sistema. + //funkciju koristiti u konstruktoru klase Korisnik za inicijalizaciju atributa _sifra. + if (GenerisiSifru("Amina Buric", 3) == "CW-AB-003/2026") + cout << "Sifra OK" << crt; + if (GenerisiSifru("Amar Macic", 15) == "CW-AM-015/2026") + cout << "Sifra OK" << crt; + if (GenerisiSifru("Maid Ramic", 156) == "CW-MR-156/2026") + cout << "Sifra OK" << crt; + //koristeci regex, funkcija ValidirajSifru provjerava da li je sifra zapisana u prethodno + //definisanom formatu. funkcija vraca true ako je sifra validna, u suprotnom vraca false. + if (ValidirajSifru("CW-AB-003/2026")) + cout << "SIFRA VALIDNA" << crt; + if (!ValidirajSifru("CW-Ab-003/2026") && !ValidirajSifru("CW-AB03/2026") && + !ValidirajSifru("CW/AB-003-2026")) + cout << "SIFRA NIJE VALIDNA" << crt; + Kolekcija termini; + for (int i = 0; i < 8; i++) + termini.Dodaj(i, "Termin_" + to_string(i)); + cout << termini << crt; + //DodajNaPoziciju dodaje novi par na lokaciju definisanu prvim parametrom, pomjera postojece + //elemente udesno i vraca trenutno stanje kolekcije. u slucaju popunjene kolekcije ili + //neispravne lokacije potrebno je baciti izuzetak. + Kolekcija prosireniTermini = termini.DodajNaPoziciju(2, 99, "Poseban termin"); + cout << prosireniTermini << crt; + //UkloniRaspon od lokacije definisane prvim parametrom uklanja broj elemenata definisan + //drugim parametrom, a vraca pokazivac na novu kolekciju koja sadrzi uklonjene elemente. + //pozivalac je odgovoran za dealokaciju vracene kolekcije. + Kolekcija* uklonjeniTermini = prosireniTermini.UkloniRaspon(3, 2); + + cout << "Uklonjeni elementi:" << crt << *uklonjeniTermini; + cout << "Preostali elementi:" << crt << prosireniTermini; + delete uklonjeniTermini; + + try { + //za neispravan raspon potrebno je baciti izuzetak + termini.UkloniRaspon(6, 5); + } + catch (exception& e) { + cout << "Exception: " << e.what() << crt; + } + DatumVrijeme vrijeme1(10, 9, 2026, 9, 0), vrijeme2(10, 9, 2026, 9, 30), + vrijeme3(10, 9, 2026, 10, 0), vrijeme4(10, 9, 2026, 12, 0); + Rezervacija salaA("SALA-A", SALA, vrijeme1, 60); + Rezervacija studio1("STUDIO-1", STUDIO, vrijeme2, 90); + Rezervacija radnoMjesto("RM-12", RADNO_MJESTO, vrijeme3, 60); + Rezervacija laboratorija("LAB-1", LABORATORIJA, vrijeme4, 400); + //ToString metoda vraca podatke o rezervaciji u formatu prikazanom u nastavku. + //voditi racuna o prikazu jednocifrenih vrijednosti datuma i vremena(npr. 9 -> 09). + cout << salaA.ToString() << crt; + //10.09.2026 09:00 SALA-A SALA 60 min + //ImaKonfliktSa vraca true ako su rezervacije istog datuma i njihovi vremenski intervali + //se preklapaju. rezervacija koja pocinje u trenutku kada prethodna zavrsava nije konfliktna. + if (salaA.ImaKonfliktSa(studio1)) + cout << "Termini se preklapaju" << crt; + if (!salaA.ImaKonfliktSa(radnoMjesto)) + cout << "Termini se ne preklapaju" << crt; + Korisnik amina("Amina Buric"), goran("Goran Skondric"), berun("Berun Agic"); + //DodajRezervaciju dodaje rezervaciju korisniku ako se ona ne preklapa sa nekom od ranije + //dodanih rezervacija i ako ukupno trajanje svih rezervacija korisnika u jednom danu ne + //prelazi 480 minuta. metoda vraca true ako je rezervacija dodana, u suprotnom vraca false. + if (amina.DodajRezervaciju(salaA)) + cout << "Rezervacija dodana" << crt; + if (!amina.DodajRezervaciju(studio1)) + cout << "Rezervacija nije dodana - preklapanje termina" << + crt; + if (amina.DodajRezervaciju(radnoMjesto)) + cout << "Rezervacija dodana" << crt; + if (!amina.DodajRezervaciju(laboratorija)) + cout << "Rezervacija nije dodana - prekoracen dnevni limit" + << crt; + CentarZaRad radniKutak("Radni kutak"), poslovnaZona("Poslovna zona"); + radniKutak.DodajKorisnika(amina); + radniKutak.DodajKorisnika(goran); + poslovnaZona.DodajKorisnika(berun); + try { + //DodajKorisnika onemogucava dodavanje korisnika sa istom sifrom i baca izuzetak + radniKutak.DodajKorisnika(amina); + } + catch (exception& e) { + cout << "Exception: " << e.what() << crt; + } + Rezervacija goranovaSala("SALA-B", SALA, vrijeme4, 120); + //RegistrujRezervaciju pronalazi korisnika na osnovu sifre i dodaje mu rezervaciju. + //i dalje vaze pravila definisana u metodi DodajRezervaciju. metoda vraca true ili false. + if (radniKutak.RegistrujRezervaciju(goran.GetSifra(), goranovaSala)) + cout << "Rezervacija registrovana" << crt; + //AktivniKorisnici vraca pokazivace na korisnike koji imaju najmanje onoliko rezervacija + //koliko je definisano vrijednoscu proslijedjenog parametra. + vector aktivni = radniKutak.AktivniKorisnici(1); + for (auto korisnik : aktivni) + cout << korisnik->GetImePrezime() << " ima " << korisnik->GetRezervacije().size() << " rezervacija" << crt; + //KoristenjePoTipu vraca kolekciju parova (korisnik, broj minuta) za sve korisnike koji + //imaju najmanje jednu rezervaciju prostora proslijedjenog tipa. + Kolekcija koristenjeSala = radniKutak.KoristenjePoTipu(SALA); + for (int i = 0; i < koristenjeSala.GetTrenutno(); i++) + cout << koristenjeSala.GetPrvi(i).GetImePrezime() << " -> " << koristenjeSala.GetDrugi(i) << " minuta" << crt; + + vector centri; + centri.push_back(radniKutak); + centri.push_back(poslovnaZona); + /* + Funkcija UcitajPodatke ucitava podatke o centrima za rad i + njihovim korisnicima iz + datoteke cije ime se prosljedjuje kao prvi parametar. Svaka linija + je zapisana u formatu: + naziv centra|ime i prezime korisnika + Za svaki ispravan red potrebno je: + - pronaci postojeci ili kreirati novi centar za rad, + - kreirati i dodati korisnika u odgovarajuci centar, + - onemoguciti dupliranje centara i korisnika unutar istog + centra. + Funkcija vraca true ako je ucitan najmanje jedan novi podatak, a + false ako datoteka ne + postoji ili nije ucitan nijedan novi podatak. + Primjer sadrzaja datoteke: + Fakultet informacijskih tehnologija + Pismeni dio ispita iz predmeta Programiranje II – 25.06.2026 + 7 + Radni kutak|Emina Junuz + Radni kutak|Jasmin Azemovic + Poslovna zona|Zanin Vejzovic + */ + + if (UcitajPodatke("korisnici.txt", centri)) + cout << "Ucitavanje uspjesno" << crt; + for (auto& centar : centri) + cout << centar.GetNaziv() << " sa " << + centar.GetKorisnici().size() << " korisnika" << crt; + cin.get(); + return 0; +} diff --git a/Ispiti/2026-06-25/PR_2026-06-25_G1_Postavka.cpp b/Ispiti/2026-06-25/PR_2026-06-25_G1_Postavka.cpp new file mode 100644 index 0000000..b437a92 --- /dev/null +++ b/Ispiti/2026-06-25/PR_2026-06-25_G1_Postavka.cpp @@ -0,0 +1,288 @@ +#include +using namespace std; + +string crt = "\n------------------------------------------------------ -------------------------- - \n"; + +string PORUKA_TELEFON = crt + +"TELEFONE ISKLJUCITE I ODLOZITE U TORBU, DZEP ILI DRUGU LOKACIJU VAN DOHVATA.\n" +"CESTO SE NA TELEFONIMA (PRO)NALAZE PROGRAMSKI KODOVI KOJI MOGU BITI ISKORISTENI ZA\n" +"RJESAVANJE ISPITNOG ZADATKA, STO CE, U SLUCAJU PRONALASKA, BITI SANKCIONISANO." + crt; + +string PORUKA_ISPIT = crt + +"0. PROVJERITE DA LI ZADACI PRIPADAJU VASOJ GRUPI (G1/G2)\n" +"1. SVE KLASE SA DINAMICKOM ALOKACIJOM MORAJU IMATI ISPRAVAN DESTRUKTOR\n" +"2. IZOSTAVLJANJE DESTRUKTORA ILI NJEGOVIH DIJELOVA BIT CE OZNACENO KAO TM\n" +"3. ATRIBUTI, METODE I PARAMETRI MORAJU BITI IDENTICNI ONIMA U TESTNOJ MAIN FUNKCIJI, \n" +" OSIM AKO POSTOJI JASNO OPISAN RAZLOG ZA MODIFIKACIJU\n" +"4. IZUZETKE BACAJTE SAMO TAMO GDJE JE IZRICITO NAGLASENO\n" +"5. SVE METODE KOJE SE POZIVAJU U MAIN-U MORAJU POSTOJATI.\n" +" AKO NEMATE ZELJENU IMPLEMENTACIJU, OSTAVITE PRAZNO TIJELO ILI VRATITE DEFAULT VRIJEDNOST\n" +"6. U MAIN FUNKCIJI MOZETE DODAVATI TESTNE PODATKE I POZIVE PO VLASTITOM IZBORU\n" +"7. TESTIRAJTE PROGRAM U OBA MODA (F5 i Ctrl+F5)" + crt; + +char* AlocirajTekst(const char* tekst) { + if (!tekst) return nullptr; + size_t vel = strlen(tekst) + 1; + char* temp = new char[vel]; + strcpy_s(temp, vel, tekst); + return temp; +} + +string GenerisiSifru(const char* imePrezime, int redniBroj); + +bool ValidirajSifru(const string& sifra); + +enum TipProstora { RADNO_MJESTO, SALA, STUDIO, LABORATORIJA }; + +const char* TipProstoraNazivi[] = { "RADNO MJESTO", "SALA", "STUDIO", "LABORATORIJA" }; + +template +class Kolekcija { + T1 _prvi[max]; + T2 _drugi[max]; + int* _trenutno; +public: + Kolekcija() { _trenutno = new int(0); } + ~Kolekcija() { delete _trenutno; _trenutno = nullptr; } + int GetTrenutno() const { return *_trenutno; } + T1& GetPrvi(int indeks) { return _prvi[indeks]; } + T2& GetDrugi(int indeks) { return _drugi[indeks]; } + T1& operator[](int indeks) { return _prvi[indeks]; } + friend ostream& operator<<(ostream& COUT, Kolekcija& obj) { + for (int i = 0; i < obj.GetTrenutno(); i++) + COUT << obj.GetPrvi(i) << " " << obj.GetDrugi(i) << endl; + return COUT; + } +}; + +class DatumVrijeme { + int* _godina, * _mjesec, * _dan, * _sati, * _minute; +public: + DatumVrijeme(int dan = 1, int mjesec = 1, int godina = 2000, int + sati = 0, int minute = 0) { + _godina = new int(godina); + _mjesec = new int(mjesec); + _dan = new int(dan); + _sati = new int(sati); + _minute = new int(minute); + } + ~DatumVrijeme() { + delete _godina; delete _mjesec; delete _dan; + delete _sati; delete _minute; + } + friend ostream& operator<<(ostream& COUT, DatumVrijeme& obj) { + COUT << *obj._dan << "." << *obj._mjesec << "." << + *obj._godina << " " << *obj._sati << ":" << *obj._minute; + return COUT; + } +}; +class Rezervacija { + char* _oznaka; + TipProstora _tipProstora; + DatumVrijeme _pocetak; + int _trajanjeMinuta; +public: + Rezervacija(const char* oznaka, TipProstora tipProstora, + DatumVrijeme pocetak, int trajanjeMinuta) + : _tipProstora(tipProstora), _pocetak(pocetak), + _trajanjeMinuta(trajanjeMinuta) { + _oznaka = AlocirajTekst(oznaka); + } + ~Rezervacija() { delete[] _oznaka; _oznaka = nullptr; } + const char* GetOznaka() const { return _oznaka; } + TipProstora GetTipProstora() const { return _tipProstora; } + DatumVrijeme& GetPocetak() { return _pocetak; } + int GetTrajanjeMinuta() const { return _trajanjeMinuta; } +}; + +class Korisnik { + static int _id; + char* _sifra; + char* _imePrezime; + vector _rezervacije; +public: + Korisnik(const char* imePrezime = "") { + _imePrezime = AlocirajTekst(imePrezime); + _sifra = AlocirajTekst(GenerisiSifru(imePrezime, + _id).c_str()); + _id++; + } + ~Korisnik() { + delete[] _sifra; _sifra = nullptr; + delete[] _imePrezime; _imePrezime = nullptr; + } + const char* GetSifra() const { return _sifra; } + const char* GetImePrezime() const { return _imePrezime; } + vector& GetRezervacije() { return _rezervacije; } + friend ostream& operator<<(ostream& COUT, Korisnik& obj) { + COUT << obj._imePrezime << " [" << obj._sifra << "]" << endl; + for (auto& rezervacija : obj._rezervacije) + //ToString metoda klase Rezervacija vraca podatke o rezervaciji u formatu : + //10.09.2026 09:00 SALA-A SALA 60 min + COUT << " - " << rezervacija.ToString() << endl; + return COUT; + } +}; + +class CentarZaRad { + char* _naziv; + vector _korisnici; +public: + CentarZaRad(const char* naziv) { _naziv = AlocirajTekst(naziv); } + ~CentarZaRad() { delete[] _naziv; _naziv = nullptr; } + CentarZaRad(const CentarZaRad& obj) { + _naziv = AlocirajTekst(obj._naziv); + _korisnici = obj._korisnici; + } + const char* GetNaziv() const { return _naziv; } + vector& GetKorisnici() { return _korisnici; } +}; + +const char* GetOdgovorNaPrvoPitanje() { + cout << "Pitanje -> Pojasnite razliku izmedju koristenja kljucne rijeci abstract i cistih virtualnih metoda ? \n"; + return "Odgovor -> OVDJE UNESITE VAS ODGOVOR"; +} + +const char* GetOdgovorNaDrugoPitanje() { + cout << "Pitanje -> Pojasnite korelaciju izmedju polimorfizma i virtualnih metoda, te zbog cega se javlja potreba za virtualnim destruktorom ? \n"; + return "Odgovor -> OVDJE UNESITE VAS ODGOVOR"; +} + +int main() { + cout << PORUKA_TELEFON; cin.get(); + cout << PORUKA_ISPIT; cin.get(); system("cls"); + cout << GetOdgovorNaPrvoPitanje() << crt; + cin.get(); + cout << GetOdgovorNaDrugoPitanje() << crt; + cin.get(); + //funkcija generise sifru korisnika na osnovu imena i prezimena, rednog broja i trenutne godine. + //sifra je u formatu CW-IN-BBB/GGGG, gdje IN predstavlja inicijale, BBB redni broj korisnika + //popunjen nulama na slobodnim mjestima, a GGGG trenutnu godinu dobijenu iz sistema. + //funkciju koristiti u konstruktoru klase Korisnik za inicijalizaciju atributa _sifra. + if (GenerisiSifru("Amina Buric", 3) == "CW-AB-003/2026") + cout << "Sifra OK" << crt; + if (GenerisiSifru("Amar Macic", 15) == "CW-AM-015/2026") + cout << "Sifra OK" << crt; + if (GenerisiSifru("Maid Ramic", 156) == "CW-MR-156/2026") + cout << "Sifra OK" << crt; + //koristeci regex, funkcija ValidirajSifru provjerava da li je sifra zapisana u prethodno + //definisanom formatu. funkcija vraca true ako je sifra validna, u suprotnom vraca false. + if (ValidirajSifru("CW-AB-003/2026")) + cout << "SIFRA VALIDNA" << crt; + if (!ValidirajSifru("CW-Ab-003/2026") && !ValidirajSifru("CW-AB03/2026") && + !ValidirajSifru("CW/AB-003-2026")) + cout << "SIFRA NIJE VALIDNA" << crt; + Kolekcija termini; + for (int i = 0; i < 8; i++) + termini.Dodaj(i, "Termin_" + to_string(i)); + cout << termini << crt; + //DodajNaPoziciju dodaje novi par na lokaciju definisanu prvim parametrom, pomjera postojece + //elemente udesno i vraca trenutno stanje kolekcije. u slucaju popunjene kolekcije ili + //neispravne lokacije potrebno je baciti izuzetak. + Kolekcija prosireniTermini = termini.DodajNaPoziciju(2, 99, "Poseban termin"); + cout << prosireniTermini << crt; + //UkloniRaspon od lokacije definisane prvim parametrom uklanja broj elemenata definisan + //drugim parametrom, a vraca pokazivac na novu kolekciju koja sadrzi uklonjene elemente. + //pozivalac je odgovoran za dealokaciju vracene kolekcije. + Kolekcija* uklonjeniTermini = prosireniTermini.UkloniRaspon(3, 2); + + cout << "Uklonjeni elementi:" << crt << *uklonjeniTermini; + cout << "Preostali elementi:" << crt << prosireniTermini; + delete uklonjeniTermini; + + try { + //za neispravan raspon potrebno je baciti izuzetak + termini.UkloniRaspon(6, 5); + } + catch (exception& e) { + cout << "Exception: " << e.what() << crt; + } + DatumVrijeme vrijeme1(10, 9, 2026, 9, 0), vrijeme2(10, 9, 2026, 9, 30), + vrijeme3(10, 9, 2026, 10, 0), vrijeme4(10, 9, 2026, 12, 0); + Rezervacija salaA("SALA-A", SALA, vrijeme1, 60); + Rezervacija studio1("STUDIO-1", STUDIO, vrijeme2, 90); + Rezervacija radnoMjesto("RM-12", RADNO_MJESTO, vrijeme3, 60); + Rezervacija laboratorija("LAB-1", LABORATORIJA, vrijeme4, 400); + //ToString metoda vraca podatke o rezervaciji u formatu prikazanom u nastavku. + //voditi racuna o prikazu jednocifrenih vrijednosti datuma i vremena(npr. 9 -> 09). + cout << salaA.ToString() << crt; + //10.09.2026 09:00 SALA-A SALA 60 min + //ImaKonfliktSa vraca true ako su rezervacije istog datuma i njihovi vremenski intervali + //se preklapaju. rezervacija koja pocinje u trenutku kada prethodna zavrsava nije konfliktna. + if (salaA.ImaKonfliktSa(studio1)) + cout << "Termini se preklapaju" << crt; + if (!salaA.ImaKonfliktSa(radnoMjesto)) + cout << "Termini se ne preklapaju" << crt; + Korisnik amina("Amina Buric"), goran("Goran Skondric"), berun("Berun Agic"); + //DodajRezervaciju dodaje rezervaciju korisniku ako se ona ne preklapa sa nekom od ranije + //dodanih rezervacija i ako ukupno trajanje svih rezervacija korisnika u jednom danu ne + //prelazi 480 minuta. metoda vraca true ako je rezervacija dodana, u suprotnom vraca false. + if (amina.DodajRezervaciju(salaA)) + cout << "Rezervacija dodana" << crt; + if (!amina.DodajRezervaciju(studio1)) + cout << "Rezervacija nije dodana - preklapanje termina" << + crt; + if (amina.DodajRezervaciju(radnoMjesto)) + cout << "Rezervacija dodana" << crt; + if (!amina.DodajRezervaciju(laboratorija)) + cout << "Rezervacija nije dodana - prekoracen dnevni limit" + << crt; + CentarZaRad radniKutak("Radni kutak"), poslovnaZona("Poslovna zona"); + radniKutak.DodajKorisnika(amina); + radniKutak.DodajKorisnika(goran); + poslovnaZona.DodajKorisnika(berun); + try { + //DodajKorisnika onemogucava dodavanje korisnika sa istom sifrom i baca izuzetak + radniKutak.DodajKorisnika(amina); + } + catch (exception& e) { + cout << "Exception: " << e.what() << crt; + } + Rezervacija goranovaSala("SALA-B", SALA, vrijeme4, 120); + //RegistrujRezervaciju pronalazi korisnika na osnovu sifre i dodaje mu rezervaciju. + //i dalje vaze pravila definisana u metodi DodajRezervaciju. metoda vraca true ili false. + if (radniKutak.RegistrujRezervaciju(goran.GetSifra(), goranovaSala)) + cout << "Rezervacija registrovana" << crt; + //AktivniKorisnici vraca pokazivace na korisnike koji imaju najmanje onoliko rezervacija + //koliko je definisano vrijednoscu proslijedjenog parametra. + vector aktivni = radniKutak.AktivniKorisnici(1); + for (auto korisnik : aktivni) + cout << korisnik->GetImePrezime() << " ima " << korisnik -> GetRezervacije().size() << " rezervacija" << crt; + //KoristenjePoTipu vraca kolekciju parova (korisnik, broj minuta) za sve korisnike koji + //imaju najmanje jednu rezervaciju prostora proslijedjenog tipa. + Kolekcija koristenjeSala = radniKutak.KoristenjePoTipu(SALA); + for (int i = 0; i < koristenjeSala.GetTrenutno(); i++) + cout << koristenjeSala.GetPrvi(i).GetImePrezime() << " -> " << koristenjeSala.GetDrugi(i) << " minuta" << crt; + vector centri; + centri.push_back(radniKutak); + centri.push_back(poslovnaZona); + /* + Funkcija UcitajPodatke ucitava podatke o centrima za rad i + njihovim korisnicima iz + datoteke cije ime se prosljedjuje kao prvi parametar. Svaka linija + je zapisana u formatu: + naziv centra|ime i prezime korisnika + Za svaki ispravan red potrebno je: + - pronaci postojeci ili kreirati novi centar za rad, + - kreirati i dodati korisnika u odgovarajuci centar, + - onemoguciti dupliranje centara i korisnika unutar istog + centra. + Funkcija vraca true ako je ucitan najmanje jedan novi podatak, a + false ako datoteka ne + postoji ili nije ucitan nijedan novi podatak. + Primjer sadrzaja datoteke: + Fakultet informacijskih tehnologija + Pismeni dio ispita iz predmeta Programiranje II – 25.06.2026 + 7 + Radni kutak|Emina Junuz + Radni kutak|Jasmin Azemovic + Poslovna zona|Zanin Vejzovic + */ + if (UcitajPodatke("korisnici.txt", centri)) + cout << "Ucitavanje uspjesno" << crt; + for (auto& centar : centri) + cout << centar.GetNaziv() << " sa " << + centar.GetKorisnici().size() << " korisnika" << crt; + cin.get(); + return 0; +} diff --git a/Ispiti/2026-06-25/README.md b/Ispiti/2026-06-25/README.md new file mode 100644 index 0000000..4b79dbc --- /dev/null +++ b/Ispiti/2026-06-25/README.md @@ -0,0 +1,12 @@ +# Grupa 1 (G1) + + +- enum TipProstora { RADNO_MJESTO, SALA, STUDIO, LABORATORIJA }; +- Kolekcija staticki niz objekata (T[max]) +- String, vector, regex, file streams, string streams, localtime, generisi sifru +- pazite na shallow copies mogu bas oduzeti vremena + + +# Grupa 2 (G2) + +uradit cu From ba94f89248e80a4a19b700fae1cb39f632066773 Mon Sep 17 00:00:00 2001 From: null68 Date: Sun, 5 Jul 2026 02:37:55 +0200 Subject: [PATCH 2/6] feat: possible exam solution G2 --- .../2026-06-25/PR2_2026-06-25_G2_Postavka.cpp | 282 ++++++++ .../PR2_2026-06-25_G2_Rje\305\241enje.cpp" | 640 ++++++++++++++++++ Ispiti/2026-06-25/README.md | 5 +- 3 files changed, 926 insertions(+), 1 deletion(-) create mode 100644 Ispiti/2026-06-25/PR2_2026-06-25_G2_Postavka.cpp create mode 100644 "Ispiti/2026-06-25/PR2_2026-06-25_G2_Rje\305\241enje.cpp" diff --git a/Ispiti/2026-06-25/PR2_2026-06-25_G2_Postavka.cpp b/Ispiti/2026-06-25/PR2_2026-06-25_G2_Postavka.cpp new file mode 100644 index 0000000..c223ccb --- /dev/null +++ b/Ispiti/2026-06-25/PR2_2026-06-25_G2_Postavka.cpp @@ -0,0 +1,282 @@ +#include +using namespace std; + +string crt = "\n---------------------------------------------------------------------------------\n"; + +string PORUKA_TELEFON = crt + +"TELEFONE ISKLJUCITE I ODLOZITE U TORBU, DZEP ILI DRUGU LOKACIJU VAN DOHVATA.\n" +"CESTO SE NA TELEFONIMA (PRO)NALAZE PROGRAMSKI KODOVI KOJI MOGU BITI ISKORISTENI ZA\n" +"RJESAVANJE ISPITNOG ZADATKA, STO CE, U SLUCAJU PRONALASKA, BITI SANKCIONISANO." + crt; + +string PORUKA_ISPIT = crt + +"0. PROVJERITE DA LI ZADACI PRIPADAJU VASOJ GRUPI (G1/G2)\n" +"1. SVE KLASE SA DINAMICKOM ALOKACIJOM MORAJU IMATI ISPRAVAN DESTRUKTOR\n" +"2. IZOSTAVLJANJE DESTRUKTORA ILI NJEGOVIH DIJELOVA BIT CE OZNACENO KAO TM\n" +"3. ATRIBUTI, METODE I PARAMETRI MORAJU BITI IDENTICNI ONIMA U TESTNOJ MAIN FUNKCIJI, \n" +" OSIM AKO POSTOJI JASNO OPISAN RAZLOG ZA MODIFIKACIJU\n" +"4. IZUZETKE BACAJTE SAMO TAMO GDJE JE IZRICITO NAGLASENO\n" +"5. SVE METODE KOJE SE POZIVAJU U MAIN-U MORAJU POSTOJATI.\n" +" AKO NEMATE ZELJENU IMPLEMENTACIJU, OSTAVITE PRAZNO TIJELO ILI VRATITE DEFAULT VRIJEDNOST\n" +"6. U MAIN FUNKCIJI MOZETE DODAVATI TESTNE PODATKE I POZIVE PO VLASTITOM IZBORU\n" +"7. TESTIRAJTE PROGRAM U OBA MODA (F5 i Ctrl+F5)" + crt; + +char* AlocirajTekst(const char* tekst) { + if (!tekst) return nullptr; + size_t vel = strlen(tekst) + 1; + char* temp = new char[vel]; + strcpy_s(temp, vel, tekst); + return temp; +} + +string GenerisiClanskiBroj(const char* imePrezime, int redniBroj); + +bool ValidirajClanskiBroj(const string& clanskiBroj); + +enum Zanr { + ROMAN, STRUCNA_LITERATURA, BIOGRAFIJA, POEZIJA, + DJECIJA_KNJIGA +}; + +const char* ZanrNazivi[] = { "ROMAN", "STRUCNA LITERATURA", "BIOGRAFIJA", "POEZIJA", "DJECIJA KNJIGA" }; + +template +class Kolekcija { + T1 * _prvi; + T2* _drugi; + int _trenutno; +public: + Kolekcija() : _prvi(nullptr), _drugi(nullptr), _trenutno(0) {} + int GetTrenutno() const { return _trenutno; } + T1& GetPrvi(int indeks) { return _prvi[indeks]; } + T2& GetDrugi(int indeks) { return _drugi[indeks]; } + T1& operator[](int indeks) { return _prvi[indeks]; } + friend ostream& operator<<(ostream& COUT, Kolekcija& obj) { + for (int i = 0; i < obj.GetTrenutno(); i++) + COUT << obj.GetPrvi(i) << " " << obj.GetDrugi(i) << endl; + return COUT; + } + ~Kolekcija() { + delete[] _prvi; _prvi = nullptr; + delete[] _drugi; _drugi = nullptr; + } +}; + +class Datum { + int* _godina, * _mjesec, * _dan; +public: + Datum(int dan = 1, int mjesec = 1, int godina = 2000) { + _godina = new int(godina); + _mjesec = new int(mjesec); + _dan = new int(dan); + } + ~Datum() { + delete _godina; delete _mjesec; delete _dan; + } +}; + +class Posudba { + char* _naslov; + Zanr _zanr; + Datum _datumPosudbe; + int _brojDana; +public: + Posudba(const char* naslov, Zanr zanr, Datum datumPosudbe, int + brojDana) + : _zanr(zanr), _datumPosudbe(datumPosudbe), + _brojDana(brojDana) { + _naslov = AlocirajTekst(naslov); + } + ~Posudba() { delete[] _naslov; _naslov = nullptr; } + const char* GetNaslov() const { return _naslov; } + Zanr GetZanr() const { return _zanr; } + Datum& GetDatumPosudbe() { return _datumPosudbe; } + int GetBrojDana() const { return _brojDana; } +}; + +class ClanBiblioteke { + static int _id; + char* _clanskiBroj; + char* _imePrezime; + vector _posudbe; +public: + ClanBiblioteke(const char* imePrezime = "") { + _imePrezime = AlocirajTekst(imePrezime); + _clanskiBroj = AlocirajTekst(GenerisiClanskiBroj(imePrezime, + _id).c_str()); + _id++; + } + ~ClanBiblioteke() { + delete[] _clanskiBroj; _clanskiBroj = nullptr; + delete[] _imePrezime; _imePrezime = nullptr; + } + const char* GetClanskiBroj() const { return _clanskiBroj; } + const char* GetImePrezime() const { return _imePrezime; } + vector& GetPosudbe() { return _posudbe; } + friend ostream& operator<<(ostream& COUT, ClanBiblioteke& obj) { + COUT << obj._imePrezime << " [" << obj._clanskiBroj << "]" << + endl; + for (auto& posudba : obj._posudbe) + //ToString metoda vraca podatke o posudbi u formatu prikazanom u nastavku. + //voditi racuna o prikazu jednocifrenih vrijednosti datuma(npr. 5 -> 05). + //05.10.2026 Tvrdjava ROMAN 14 dana + COUT << " - " << posudba.ToString() << endl; + return COUT; + } +}; +int ClanBiblioteke::_id = 1; + +class Biblioteka { + char* _naziv; + vector _clanovi; +public: + Biblioteka(const char* naziv) { _naziv = AlocirajTekst(naziv); } + ~Biblioteka() { delete[] _naziv; _naziv = nullptr; } + Biblioteka(const Biblioteka& obj) { + _naziv = AlocirajTekst(obj._naziv); + _clanovi = obj._clanovi; + } + const char* GetNaziv() const { return _naziv; } + vector& GetClanovi() { return _clanovi; } +}; + +const char* GetOdgovorNaPrvoPitanje() { + cout << "Pitanje -> Pojasnite kako tip nasljeđivanja (public, protected, private) utice na dostupnost clanova bazne klase u izvedenoj klasi.Sta se desava sa public i protected clanovima bazne klase ako se nasljeđivanje izvrsi kao protected ili private ? .\n"; + return "Odgovor -> OVDJE UNESITE VAS ODGOVOR"; +} + +const char* GetOdgovorNaDrugoPitanje() { + cout << "Pitanje -> Pojasnite ulogu i znacaj move konstruktora, kada se poziva i po cemu se razlikuje od konstruktora kopije.\n"; + return "Odgovor -> OVDJE UNESITE VAS ODGOVOR"; +} + +int main() { + cout << PORUKA_TELEFON; cin.get(); + cout << PORUKA_ISPIT; cin.get(); system("cls"); + cout << GetOdgovorNaPrvoPitanje() << crt; + cin.get(); + cout << GetOdgovorNaDrugoPitanje() << crt; + cin.get(); + //funkcija generise clanski broj na osnovu imena i prezimena, rednog broja i trenutne godine. + //clanski broj je u formatu GGGG/IN-BBB, gdje GGGG predstavlja trenutnu godinu, IN inicijale, + //a BBB redni broj clana popunjen nulama na tri mjesta. + //funkciju koristiti u konstruktoru klase ClanBiblioteke za inicijalizaciju atributa _clanskiBroj. + if (GenerisiClanskiBroj("Amina Buric", 3) == "2026/AB-003") + cout << "Clanski broj OK" << crt; + if (GenerisiClanskiBroj("Amar Macic", 15) == "2026/AM-015") + cout << "Clanski broj OK" << crt; + if (GenerisiClanskiBroj("Maid Ramic", 156) == "2026/MR-156") + cout << "Clanski broj OK" << crt; + //ValidirajClanskiBroj koristeci regex provjerava format definisan u prethodnom dijelu zadatka. + if (ValidirajClanskiBroj("2026/AB-003")) + cout << "CLANSKI BROJ VALIDAN" << crt; + if (!ValidirajClanskiBroj("2026/Ab-003") && + !ValidirajClanskiBroj("26/AB-003") && + !ValidirajClanskiBroj("2026-AB/003")) + cout << "CLANSKI BROJ NIJE VALIDAN" << crt; + Kolekcija inventar; + for (int i = 0; i < 8; i++) + inventar.Dodaj(i, "Knjiga_" + to_string(i)); + cout << inventar << crt; + //DodajNaPoziciju dodaje novi par na lokaciju definisanu prvim parametrom.metoda vraca trenutno stanje kolekcije. + //u slucaju popunjene kolekcije ili neispravne lokacije potrebno je baciti izuzetak(za potrebe testiranja mozete dodati try catch blok). + Kolekcija prosireniInventar = inventar.DodajNaPoziciju(2, 99, "Posebno izdanje"); + cout << prosireniInventar << crt; + //UkloniRaspon uklanja broj elemenata definisan drugim parametrom, pocevsi od lokacije + //definisane prvim parametrom (ukljucujuci tu lokaciju). metoda vraca pokazivac na novu kolekciju sa uklonjenim + //elementima, a pozivalac je odgovoran za njenu dealokaciju. + Kolekcija* uklonjeneKnjige = prosireniInventar.UkloniRaspon(3, 2); + cout << "Uklonjeni elementi:" << crt << *uklonjeneKnjige; + cout << "Preostali elementi:" << crt << prosireniInventar; + delete uklonjeneKnjige; + try { + //za neispravan raspon potrebno je baciti izuzetak + inventar.UkloniRaspon(6, 5); + } + catch (exception& e) { + cout << "Exception: " << e.what() << crt; + } + Datum datum1(5, 10, 2026), datum2(7, 10, 2026), datum3(10, 10, + 2026); + Posudba tvrdjava("Tvrdjava", ROMAN, datum1, 14); + Posudba cpp("Programiranje u C++", STRUCNA_LITERATURA, datum1, + 30); + Posudba tesla("Nikola Tesla", BIOGRAFIJA, datum2, 20); + Posudba pjesme("Izabrane pjesme", POEZIJA, datum3, 10); + //ToString metoda vraca podatke o posudbi u formatu prikazanom u nastavku. + //voditi racuna o prikazu jednocifrenih vrijednosti datuma (npr. 5 -> 05). + cout << tvrdjava.ToString() << crt; + //05.10.2026 Tvrdjava ROMAN 14 dana + ClanBiblioteke amina("Amina Buric"), goran("Goran Skondric"), berun("Berun Agic"); + //DodajPosudbu dodaje posudbu ako clan nema ranije zaduzenu knjigu istog naslova, + //ako trenutno ima manje od tri posudbe i ako je broj dana posudbe u rasponu od 1 do 30. + //metoda vraca true ako je posudba dodana, u suprotnom vraca false. + if (amina.DodajPosudbu(tvrdjava)) + cout << "Posudba dodana" << crt; + if (!amina.DodajPosudbu(tvrdjava)) + cout << "Posudba nije dodana - knjiga je vec zaduzena" << + crt; + amina.DodajPosudbu(cpp); + amina.DodajPosudbu(tesla); + if (!amina.DodajPosudbu(pjesme)) + cout << "Posudba nije dodana - dostignut maksimalan broj posudbi" << crt; + //RazduziKnjigu uklanja posudbu na osnovu naslova knjige i vraca true ako je pronadjena + //i uklonjena. ukoliko knjiga nije pronadjena metoda vraca false. + if (amina.RazduziKnjigu("Tvrdjava")) + cout << "Knjiga razduzena" << crt; + if (!amina.RazduziKnjigu("Nepostojeca knjiga")) + cout << "Knjiga nije pronadjena" << crt; + Biblioteka gradska("Gradska biblioteka"), + univerzitetska("Univerzitetska biblioteka"); + gradska.DodajClana(amina); + gradska.DodajClana(goran); + univerzitetska.DodajClana(berun); + try { + //DodajClana onemogucava dodavanje clana sa istim clanskim brojem i baca izuzetak + gradska.DodajClana(amina); + } + catch (exception& e) { + cout << "Exception: " << e.what() << crt; + } + //EvidentirajPosudbu pronalazi clana na osnovu clanskog broja i dodaje mu posudbu. + //i dalje vaze pravila definisana u metodi DodajPosudbu. metoda vraca true ili false. + if (gradska.EvidentirajPosudbu(goran.GetClanskiBroj(), pjesme)) + cout << "Posudba evidentirana" << crt; + //AktivniClanovi vraca pokazivace na clanove koji imaju najmanje onoliko posudbi + //koliko je definisano vrijednoscu proslijedjenog parametra. + vector aktivni = gradska.AktivniClanovi(1); + for (auto clan : aktivni) + cout << clan->GetImePrezime() << " ima " << clan -> GetPosudbe().size() << " aktivnih posudbi" << crt; + //PosudbePoZanru vraca kolekciju parova (clan, broj posudbi) za sve clanove koji imaju + //najmanje jednu aktivnu posudbu knjige proslijedjenog zanra. + Kolekcija strucnePosudbe = gradska.PosudbePoZanru(STRUCNA_LITERATURA); + for (int i = 0; i < strucnePosudbe.GetTrenutno(); i++) + cout << strucnePosudbe.GetPrvi(i).GetImePrezime() << " -> " << strucnePosudbe.GetDrugi(i) << " posudbi" << crt; + vector biblioteke; + biblioteke.push_back(gradska); + biblioteke.push_back(univerzitetska); + /* + Funkcija UcitajPodatke ucitava podatke o bibliotekama i njihovim + clanovima iz datoteke + cije ime se prosljedjuje kao prvi parametar. Svaka linija je + zapisana u formatu: + naziv biblioteke|ime i prezime clana + Za svaki ispravan red potrebno je: + - pronaci postojecu ili kreirati novu biblioteku, + - kreirati i dodati clana u odgovarajucu biblioteku, + - onemoguciti dupliranje biblioteka i clanova unutar iste + biblioteke. + Funkcija vraca true ako je ucitan najmanje jedan novi podatak, a + false ako datoteka ne + postoji ili nije ucitan nijedan novi podatak. + Primjer sadrzaja datoteke: + Gradska biblioteka|Emina Junuz + Gradska biblioteka|Jasmin Azemovic + Univerzitetska biblioteka|Zanin Vejzovic + */ + if (UcitajPodatke("clanovi.txt", biblioteke)) + cout << "Ucitavanje uspjesno" << crt; + for (auto& biblioteka : biblioteke) + cout << biblioteka.GetNaziv() << " sa " << biblioteka.GetClanovi().size() << " clanova" << crt; + cin.get(); + return 0; +} diff --git "a/Ispiti/2026-06-25/PR2_2026-06-25_G2_Rje\305\241enje.cpp" "b/Ispiti/2026-06-25/PR2_2026-06-25_G2_Rje\305\241enje.cpp" new file mode 100644 index 0000000..d1db50f --- /dev/null +++ "b/Ispiti/2026-06-25/PR2_2026-06-25_G2_Rje\305\241enje.cpp" @@ -0,0 +1,640 @@ +#include +#include +#include +#include +#include + +//za setfill i setw +#include + +// za ostringstream +#include + +// za fajlove +#include + +using namespace std; + + +string crt = "\n---------------------------------------------------------------------------------\n"; + +string PORUKA_TELEFON = crt + +"TELEFONE ISKLJUCITE I ODLOZITE U TORBU, DZEP ILI DRUGU LOKACIJU VAN DOHVATA.\n" +"CESTO SE NA TELEFONIMA (PRO)NALAZE PROGRAMSKI KODOVI KOJI MOGU BITI ISKORISTENI ZA\n" +"RJESAVANJE ISPITNOG ZADATKA, STO CE, U SLUCAJU PRONALASKA, BITI SANKCIONISANO." + crt; + +string PORUKA_ISPIT = crt + +"0. PROVJERITE DA LI ZADACI PRIPADAJU VASOJ GRUPI (G1/G2)\n" +"1. SVE KLASE SA DINAMICKOM ALOKACIJOM MORAJU IMATI ISPRAVAN DESTRUKTOR\n" +"2. IZOSTAVLJANJE DESTRUKTORA ILI NJEGOVIH DIJELOVA BIT CE OZNACENO KAO TM\n" +"3. ATRIBUTI, METODE I PARAMETRI MORAJU BITI IDENTICNI ONIMA U TESTNOJ MAIN FUNKCIJI, \n" +" OSIM AKO POSTOJI JASNO OPISAN RAZLOG ZA MODIFIKACIJU\n" +"4. IZUZETKE BACAJTE SAMO TAMO GDJE JE IZRICITO NAGLASENO\n" +"5. SVE METODE KOJE SE POZIVAJU U MAIN-U MORAJU POSTOJATI.\n" +" AKO NEMATE ZELJENU IMPLEMENTACIJU, OSTAVITE PRAZNO TIJELO ILI VRATITE DEFAULT VRIJEDNOST\n" +"6. U MAIN FUNKCIJI MOZETE DODAVATI TESTNE PODATKE I POZIVE PO VLASTITOM IZBORU\n" +"7. TESTIRAJTE PROGRAM U OBA MODA (F5 i Ctrl+F5)" + crt; + +char* AlocirajTekst(const char* tekst) { + if (!tekst) return nullptr; + size_t vel = strlen(tekst) + 1; + char* temp = new char[vel]; + strcpy_s(temp, vel, tekst); + return temp; +} + + +string GenerisiClanskiBroj(const char* imePrezime, int redniBroj) { + ostringstream buffer; + time_t vrijeme = time(nullptr); + struct tm vrijeme_info; + localtime_s(&vrijeme_info, &vrijeme); + + string s_imePrezime = imePrezime; + + buffer << vrijeme_info.tm_year + 1900 << "/" << imePrezime[0] << imePrezime[s_imePrezime.find(" ", 1) + 1] << "-" << setfill('0') << setw(3) << redniBroj; + + return buffer.str(); +} + +bool ValidirajClanskiBroj(const string& clanskiBroj) { + return regex_match(clanskiBroj, regex("([0-9]{4})/([A-Z]{2})-([0-9]{3})")); +} + +enum Zanr { + ROMAN, STRUCNA_LITERATURA, BIOGRAFIJA, POEZIJA, + DJECIJA_KNJIGA +}; + +const char* ZanrNazivi[] = { "ROMAN", "STRUCNA LITERATURA", "BIOGRAFIJA", "POEZIJA", "DJECIJA KNJIGA" }; + +const char* GetZanrCStr(Zanr zanr) { + return ZanrNazivi[zanr]; +} + +template +class Kolekcija { + T1* _prvi; + T2* _drugi; + int _trenutno; +public: + Kolekcija() : _prvi(nullptr), _drugi(nullptr), _trenutno(0) {} + int GetTrenutno() const { return _trenutno; } + T1& GetPrvi(int indeks) { return _prvi[indeks]; } + T2& GetDrugi(int indeks) { return _drugi[indeks]; } + T1& operator[](int indeks) { return _prvi[indeks]; } + friend ostream& operator<<(ostream& COUT, Kolekcija& obj) { + for (int i = 0; i < obj.GetTrenutno(); i++) + COUT << obj.GetPrvi(i) << " " << obj.GetDrugi(i) << endl; + return COUT; + } + ~Kolekcija() { + delete[] _prvi; _prvi = nullptr; + delete[] _drugi; _drugi = nullptr; + } + + // methods i added below + + Kolekcija(const Kolekcija& other) { + _trenutno = other.GetTrenutno(); + + _prvi = new T1[_trenutno]; + _drugi = new T2[_trenutno]; + for (int i = 0; i < _trenutno; i++) { + _prvi[i] = other._prvi[i]; + _drugi[i] = other._drugi[i]; + } + } + + Kolekcija& operator=(const Kolekcija& other) { + if (this == &other) return *this; + + delete[] _prvi; + delete[] _drugi; + + _trenutno = other.GetTrenutno(); + + _prvi = new T1[_trenutno]; + _drugi = new T2[_trenutno]; + for (int i = 0; i < _trenutno; i++) { + _prvi[i] = other._prvi[i]; + _drugi[i] = other._drugi[i]; + } + + return *this; + } + + + // ja mislim da je ovo najtezi oblik kolekcije + void Dodaj(T1 el1, T2 el2) { + if (_trenutno >= max) return; + + if (_prvi == nullptr) { + _prvi = new T1[1]; + _drugi = new T2[2]; + } + else { + // pravim kopije adresa nizova zbog sljedeceg koraka + T1* temp1 = _prvi; + T2* temp2 = _drugi; + + // "stari" pointeri sada pokazuju na novi prazan niz, u proslom koraku sam sacuvao stare adrese i sada mogu sve iz stare adrese ubacit u ove nove + _prvi = new T1[_trenutno + 1]; + _drugi = new T2[_trenutno + 1]; + + for (int i = 0; i < _trenutno; i++) { + _prvi[i] = temp1[i]; + _drugi[i] = temp2[i]; + } + // i sada se rjesavam onih nizova prije ove alokacije iznad for loopa + delete[] temp1; + delete[] temp2; + } + // sada kada imam novi prosiren niz mogu da dodam nove elemente + _prvi[_trenutno] = el1; + _drugi[_trenutno] = el2; + _trenutno++; + } + + Kolekcija DodajNaPoziciju(int pos, T1 el1, T2 el2) { + if (pos < 0 || pos > _trenutno) throw exception("invalid position - DodajNaPoziciju"); + Kolekcija nova; + + for (int i = 0; i < _trenutno; i++) { + if (i == pos) { + nova.Dodaj(el1, el2); + } + nova.Dodaj(_prvi[i], _drugi[i]); + } + + *this = nova; + return nova; + } + + // ne zelim ni da pokusavam shiftovati, a i nemam neku bolju ideju :D + Kolekcija* UkloniRaspon(int from, int elements) { + if (from < 0 || from > _trenutno || from + elements > _trenutno || elements < 1) { + throw exception("invalid range - UkloniRaspon"); + } + Kolekcija* removed = new Kolekcija(); + Kolekcija novoStanje; + + + for(int i = 0; i < _trenutno; i++) { + if(i >= from && i < from + elements) { + removed->Dodaj(_prvi[i], _drugi[i]); + } + else novoStanje.Dodaj(_prvi[i], _drugi[i]); + } + + *this = novoStanje; + + return removed; + } + +}; + +class Datum { + int* _godina, * _mjesec, * _dan; +public: + Datum(int dan = 1, int mjesec = 1, int godina = 2000) { + _godina = new int(godina); + _mjesec = new int(mjesec); + _dan = new int(dan); + } + ~Datum() { + delete _godina; delete _mjesec; delete _dan; + } + + Datum(const Datum& other) { + _godina = new int(*other._godina); + _mjesec = new int(*other._mjesec); + _dan = new int(*other._dan); + } + Datum& operator=(const Datum& other) { + if (this == &other) return *this; + + delete _godina; delete _mjesec; delete _dan; + + _godina = new int(*other._godina); + _mjesec = new int(*other._mjesec); + _dan = new int(*other._dan); + return *this; + } + + int GetDan() { + return *_dan; + } + int GetMjesec() { + return *_mjesec; + } + int GetGodina() { + return *_godina; + } +}; + +class Posudba { + char* _naslov; + Zanr _zanr; + Datum _datumPosudbe; + int _brojDana; +public: + Posudba(const char* naslov, Zanr zanr, Datum datumPosudbe, int + brojDana) + : _zanr(zanr), _datumPosudbe(datumPosudbe), + _brojDana(brojDana) { + _naslov = AlocirajTekst(naslov); + } + ~Posudba() { delete[] _naslov; _naslov = nullptr; } + const char* GetNaslov() const { return _naslov; } + Zanr GetZanr() const { return _zanr; } + Datum& GetDatumPosudbe() { return _datumPosudbe; } + int GetBrojDana() const { return _brojDana; } + + // methods i added below + + + Posudba(const Posudba& other) { + _naslov = AlocirajTekst(other.GetNaslov()); + _zanr = other._zanr; + _datumPosudbe = other._datumPosudbe; + _brojDana = other._brojDana; + } + + Posudba& operator=(const Posudba& other) { + if (this == &other) return *this; + delete[] _naslov; _naslov = nullptr; + + _naslov = AlocirajTekst(other.GetNaslov()); + _zanr = other._zanr; + _datumPosudbe = other._datumPosudbe; + _brojDana = other._brojDana; + + return *this; + } + + string ToString() { + ostringstream buffer; + + buffer << setfill('0') << setw(2) << _datumPosudbe.GetDan() << "."; + buffer << setfill('0') << setw(2) << _datumPosudbe.GetMjesec() << "."; + buffer << setfill('0') << setw(2) << _datumPosudbe.GetGodina() << " "; + + buffer << _naslov << " " << GetZanrCStr(_zanr) << " " << _brojDana << " dana"; + + + return buffer.str(); + } +}; + +class ClanBiblioteke { + static int _id; + char* _clanskiBroj; + char* _imePrezime; + vector _posudbe; +public: + ClanBiblioteke(const char* imePrezime = "") { + _imePrezime = AlocirajTekst(imePrezime); + _clanskiBroj = AlocirajTekst(GenerisiClanskiBroj(imePrezime, + _id).c_str()); + _id++; + } + ~ClanBiblioteke() { + delete[] _clanskiBroj; _clanskiBroj = nullptr; + delete[] _imePrezime; _imePrezime = nullptr; + } + const char* GetClanskiBroj() const { return _clanskiBroj; } + const char* GetImePrezime() const { return _imePrezime; } + vector& GetPosudbe() { return _posudbe; } + friend ostream& operator<<(ostream& COUT, ClanBiblioteke& obj) { + COUT << obj._imePrezime << " [" << obj._clanskiBroj << "]" << + endl; + for (auto& posudba : obj._posudbe) + //ToString metoda vraca podatke o posudbi u formatu prikazanom u nastavku. + //voditi racuna o prikazu jednocifrenih vrijednosti datuma(npr. 5 -> 05). + //05.10.2026 Tvrdjava ROMAN 14 dana + COUT << " - " << posudba.ToString() << endl; + return COUT; + } + + // methods i added below + + ClanBiblioteke(const ClanBiblioteke& other) { + _imePrezime = AlocirajTekst(other.GetImePrezime()); + _clanskiBroj = AlocirajTekst(other.GetClanskiBroj()); + _posudbe = other._posudbe; + } + + ClanBiblioteke& operator=(const ClanBiblioteke& other) { + if (this == &other) return *this; + + delete[] _clanskiBroj; _clanskiBroj = nullptr; + delete[] _imePrezime; _imePrezime = nullptr; + + _imePrezime = AlocirajTekst(other.GetImePrezime()); + _clanskiBroj = AlocirajTekst(other.GetClanskiBroj()); + _posudbe = other._posudbe; + return *this; + } + + bool PosudbaExists(const Posudba& posudba) { + for (auto& p : _posudbe) { + if (strcmp(p.GetNaslov(), posudba.GetNaslov()) == 0) return true; + } + return false; + } + + bool PosudbaExists(const char* naslov) { + for (auto& p : _posudbe) { + if (strcmp(p.GetNaslov(), naslov) == 0) return true; + } + return false; + } + + bool DodajPosudbu(const Posudba& posudba) { + if (PosudbaExists(posudba)) return false; + if (_posudbe.size() >= 3) return false; + if (posudba.GetBrojDana() < 1 || posudba.GetBrojDana() > 30) return false; + + _posudbe.push_back(posudba); + return true; + } + + int PronadjiIndexOdPosudbe(const char* naslov) { + for (int i = 0; i < _posudbe.size(); i++) { + if (strcmp(_posudbe[i].GetNaslov(), naslov) == 0) return i; + } + return -1; + } + + bool RazduziKnjigu(const char* naslov) { + if (PosudbaExists(naslov)) { + int index = PronadjiIndexOdPosudbe(naslov); + _posudbe.erase(_posudbe.begin() + index); + return true; + } + return false; + } + +}; +int ClanBiblioteke::_id = 1; + +class Biblioteka { + char* _naziv; + vector _clanovi; +public: + Biblioteka(const char* naziv) { _naziv = AlocirajTekst(naziv); } + ~Biblioteka() { delete[] _naziv; _naziv = nullptr; } + Biblioteka(const Biblioteka& obj) { + _naziv = AlocirajTekst(obj._naziv); + _clanovi = obj._clanovi; + } + const char* GetNaziv() const { return _naziv; } + vector& GetClanovi() { return _clanovi; } + + // methods i added below + + bool ClanExists(const ClanBiblioteke& clan) { + for (auto& c : _clanovi) { + if (strcmp(c.GetClanskiBroj(), clan.GetClanskiBroj()) == 0) return true; + } + return false; + } + + void DodajClana(const ClanBiblioteke& clan) { + if (ClanExists(clan)) throw exception("clan already exists - DodajClana"); + + _clanovi.push_back(clan); + } + + ClanBiblioteke* GetClan(const char* clanskiBroj) { + for (auto& c : _clanovi) { + if (strcmp(c.GetClanskiBroj(), clanskiBroj) == 0) return &c; + } + return nullptr; + } + + bool EvidentirajPosudbu(const char* clanskiBroj, Posudba posudba) { + ClanBiblioteke* clan = GetClan(clanskiBroj); + + if (!clan) return false; + + clan->DodajPosudbu(posudba); + return true; + } + + vector AktivniClanovi(int min) { + vector clanovi; + + for (auto& c : _clanovi) { + if (c.GetPosudbe().size() >= min) { + clanovi.push_back(&c); + } + } + + return clanovi; + } + + Kolekcija PosudbePoZanru(Zanr zanr) { + Kolekcija posudbe; + + for (auto& c : _clanovi) { + int kolikoPosudbi = 0; + for (auto& p : c.GetPosudbe()) { + if (p.GetZanr() == zanr) { + kolikoPosudbi += 1; + } + } + if (kolikoPosudbi > 0) { + posudbe.Dodaj(c, kolikoPosudbi); + } + } + + return posudbe; + } +}; + +const char* GetOdgovorNaPrvoPitanje() { + cout << "Pitanje -> Pojasnite kako tip nasljeđivanja (public, protected, private) utice na dostupnost clanova bazne klase u izvedenoj klasi.Sta se desava sa public i protected clanovima bazne klase ako se nasljeđivanje izvrsi kao protected ili private ? .\n"; + return "Kod public nasljeđivanja public i protected članovi ostaju public i protected u izvedenoj klasi, dok se kod protected nasljeđivanja i public i protected članovi spuštaju na protected, a kod private nasljeđivanja postaju private i nisu dostupni van klase."; +} + +const char* GetOdgovorNaDrugoPitanje() { + cout << "Pitanje -> Pojasnite ulogu i znacaj move konstruktora, kada se poziva i po cemu se razlikuje od konstruktora kopije.\n"; + return "Move konstruktor preuzima resurse privremenog objekta bez kopiranja (krađa resursa), poziva se kod rvalue objekata ili eksplicitno s std::move, i razlikuje se od copy konstruktora po tome što copy pravi duboku kopiju, dok move samo prenosi vlasništvo nad resursima."; +} + + +Biblioteka* GetBiblioteka(const char* naziv, vector& biblioteke) { + for (auto& b : biblioteke) { + if (strcmp(b.GetNaziv(), naziv) == 0) return &b; + } + return nullptr; +} + +bool UcitajPodatke(const char* nazivFajla, vector& biblioteke) { + ifstream file(nazivFajla); + + if (!file.is_open()) { + return false; + } + + string s_biblioteka; + string s_clan; + bool promjena = false; + + while(getline(file, s_biblioteka, '|') && getline(file, s_clan)) { + Biblioteka* biblioteka = GetBiblioteka(s_biblioteka.c_str(), biblioteke); + if (!biblioteka) { + biblioteke.push_back(Biblioteka(s_biblioteka.c_str())); + biblioteka = &biblioteke.back(); + ClanBiblioteke clan(s_clan.c_str()); + + try { + biblioteka->DodajClana(clan); + } + catch (exception &e) { + // ja msm da ne treba ovde nista jer je receno samo da onemoguci dupliranje clanova + } + + promjena = true; + } + + } + return promjena; +} + + +int main() { + + cout << PORUKA_TELEFON; cin.get(); + cout << PORUKA_ISPIT; cin.get(); system("cls"); + cout << GetOdgovorNaPrvoPitanje() << crt; + cin.get(); + cout << GetOdgovorNaDrugoPitanje() << crt; + cin.get(); + + //funkcija generise clanski broj na osnovu imena i prezimena, rednog broja i trenutne godine. + //clanski broj je u formatu GGGG/IN-BBB, gdje GGGG predstavlja trenutnu godinu, IN inicijale, + //a BBB redni broj clana popunjen nulama na tri mjesta. + //funkciju koristiti u konstruktoru klase ClanBiblioteke za inicijalizaciju atributa _clanskiBroj. + if (GenerisiClanskiBroj("Amina Buric", 3) == "2026/AB-003") + cout << "Clanski broj OK" << crt; + if (GenerisiClanskiBroj("Amar Macic", 15) == "2026/AM-015") + cout << "Clanski broj OK" << crt; + if (GenerisiClanskiBroj("Maid Ramic", 156) == "2026/MR-156") + cout << "Clanski broj OK" << crt; + //ValidirajClanskiBroj koristeci regex provjerava format definisan u prethodnom dijelu zadatka. + if (ValidirajClanskiBroj("2026/AB-003")) + cout << "CLANSKI BROJ VALIDAN" << crt; + if (!ValidirajClanskiBroj("2026/Ab-003") && + !ValidirajClanskiBroj("26/AB-003") && + !ValidirajClanskiBroj("2026-AB/003")) + cout << "CLANSKI BROJ NIJE VALIDAN" << crt; + Kolekcija inventar; + for (int i = 0; i < 8; i++) + inventar.Dodaj(i, "Knjiga_" + to_string(i)); + cout << inventar << crt; + //DodajNaPoziciju dodaje novi par na lokaciju definisanu prvim parametrom.metoda vraca trenutno stanje kolekcije. + //u slucaju popunjene kolekcije ili neispravne lokacije potrebno je baciti izuzetak(za potrebe testiranja mozete dodati try catch blok). + Kolekcija prosireniInventar = inventar.DodajNaPoziciju(2, 99, "Posebno izdanje"); + cout << prosireniInventar << crt; + //UkloniRaspon uklanja broj elemenata definisan drugim parametrom, pocevsi od lokacije + //definisane prvim parametrom (ukljucujuci tu lokaciju). metoda vraca pokazivac na novu kolekciju sa uklonjenim + //elementima, a pozivalac je odgovoran za njenu dealokaciju. + Kolekcija* uklonjeneKnjige = prosireniInventar.UkloniRaspon(3, 2); + cout << "Uklonjeni elementi:" << crt << *uklonjeneKnjige; + cout << "Preostali elementi:" << crt << prosireniInventar; + delete uklonjeneKnjige; + try { + //za neispravan raspon potrebno je baciti izuzetak + inventar.UkloniRaspon(6, 5); + } + catch (exception& e) { + cout << "Exception: " << e.what() << crt; + } + Datum datum1(5, 10, 2026), datum2(7, 10, 2026), datum3(10, 10, + 2026); + Posudba tvrdjava("Tvrdjava", ROMAN, datum1, 14); + Posudba cpp("Programiranje u C++", STRUCNA_LITERATURA, datum1, + 30); + Posudba tesla("Nikola Tesla", BIOGRAFIJA, datum2, 20); + Posudba pjesme("Izabrane pjesme", POEZIJA, datum3, 10); + //ToString metoda vraca podatke o posudbi u formatu prikazanom u nastavku. + //voditi racuna o prikazu jednocifrenih vrijednosti datuma (npr. 5 -> 05). + cout << tvrdjava.ToString() << crt; + //05.10.2026 Tvrdjava ROMAN 14 dana + ClanBiblioteke amina("Amina Buric"), goran("Goran Skondric"), berun("Berun Agic"); + //DodajPosudbu dodaje posudbu ako clan nema ranije zaduzenu knjigu istog naslova, + //ako trenutno ima manje od tri posudbe i ako je broj dana posudbe u rasponu od 1 do 30. + //metoda vraca true ako je posudba dodana, u suprotnom vraca false. + if (amina.DodajPosudbu(tvrdjava)) + cout << "Posudba dodana" << crt; + if (!amina.DodajPosudbu(tvrdjava)) + cout << "Posudba nije dodana - knjiga je vec zaduzena" << + crt; + amina.DodajPosudbu(cpp); + amina.DodajPosudbu(tesla); + if (!amina.DodajPosudbu(pjesme)) + cout << "Posudba nije dodana - dostignut maksimalan broj posudbi" << crt; + //RazduziKnjigu uklanja posudbu na osnovu naslova knjige i vraca true ako je pronadjena + //i uklonjena. ukoliko knjiga nije pronadjena metoda vraca false. + if (amina.RazduziKnjigu("Tvrdjava")) + cout << "Knjiga razduzena" << crt; + if (!amina.RazduziKnjigu("Nepostojeca knjiga")) + cout << "Knjiga nije pronadjena" << crt; + Biblioteka gradska("Gradska biblioteka"), + univerzitetska("Univerzitetska biblioteka"); + gradska.DodajClana(amina); + gradska.DodajClana(goran); + univerzitetska.DodajClana(berun); + try { + //DodajClana onemogucava dodavanje clana sa istim clanskim brojem i baca izuzetak + gradska.DodajClana(amina); + } + catch (exception& e) { + cout << "Exception: " << e.what() << crt; + } + //EvidentirajPosudbu pronalazi clana na osnovu clanskog broja i dodaje mu posudbu. + //i dalje vaze pravila definisana u metodi DodajPosudbu. metoda vraca true ili false. + if (gradska.EvidentirajPosudbu(goran.GetClanskiBroj(), pjesme)) + cout << "Posudba evidentirana" << crt; + //AktivniClanovi vraca pokazivace na clanove koji imaju najmanje onoliko posudbi + //koliko je definisano vrijednoscu proslijedjenog parametra. + vector aktivni = gradska.AktivniClanovi(1); + for (auto clan : aktivni) + cout << clan->GetImePrezime() << " ima " << clan->GetPosudbe().size() << " aktivnih posudbi" << crt; + //PosudbePoZanru vraca kolekciju parova (clan, broj posudbi) za sve clanove koji imaju + //najmanje jednu aktivnu posudbu knjige proslijedjenog zanra. + Kolekcija strucnePosudbe = gradska.PosudbePoZanru(STRUCNA_LITERATURA); + for (int i = 0; i < strucnePosudbe.GetTrenutno(); i++) + cout << strucnePosudbe.GetPrvi(i).GetImePrezime() << " -> " << strucnePosudbe.GetDrugi(i) << " posudbi" << crt; + vector biblioteke; + biblioteke.push_back(gradska); + biblioteke.push_back(univerzitetska); + /* + Funkcija UcitajPodatke ucitava podatke o bibliotekama i njihovim + clanovima iz datoteke + cije ime se prosljedjuje kao prvi parametar. Svaka linija je + zapisana u formatu: + naziv biblioteke|ime i prezime clana + Za svaki ispravan red potrebno je: + - pronaci postojecu ili kreirati novu biblioteku, + - kreirati i dodati clana u odgovarajucu biblioteku, + - onemoguciti dupliranje biblioteka i clanova unutar iste + biblioteke. + Funkcija vraca true ako je ucitan najmanje jedan novi podatak, a + false ako datoteka ne + postoji ili nije ucitan nijedan novi podatak. + Primjer sadrzaja datoteke: + Gradska biblioteka|Emina Junuz + Gradska biblioteka|Jasmin Azemovic + Univerzitetska biblioteka|Zanin Vejzovic + */ + if (UcitajPodatke("clanovi.txt", biblioteke)) + cout << "Ucitavanje uspjesno" << crt; + for (auto& biblioteka : biblioteke) + cout << biblioteka.GetNaziv() << " sa " << biblioteka.GetClanovi().size() << " clanova" << crt; + cin.get(); + return 0; +} diff --git a/Ispiti/2026-06-25/README.md b/Ispiti/2026-06-25/README.md index 4b79dbc..6284d3b 100644 --- a/Ispiti/2026-06-25/README.md +++ b/Ispiti/2026-06-25/README.md @@ -9,4 +9,7 @@ # Grupa 2 (G2) -uradit cu +- enum Zanr { ROMAN, STRUCNA_LITERATURA, BIOGRAFIJA, POEZIJA, DJECIJA_KNJIGA }; +- Kolekcija dinamicki rastuci niz(T*) +- String, vector, regex, file streams, string streams, localtime, generisi clanski broj, validiraj clanski broj +- po meni najtezi zadatak za kolekcije, ali cini mi se da je dole skratio malo zadatka From eaedb63e14b838a4606f5e494697e47f5673a228 Mon Sep 17 00:00:00 2001 From: null68 Date: Wed, 19 Aug 2026 19:01:14 +0200 Subject: [PATCH 3/6] feat: possible exam solutions --- .../2026-07-09/PR2_2026-07-09_G1_Postavka.cpp | 679 +++++++++++ .../PR2_2026-07-09_G1_Rje\305\241enje.cpp" | 1020 +++++++++++++++++ 2 files changed, 1699 insertions(+) create mode 100644 Ispiti/2026-07-09/PR2_2026-07-09_G1_Postavka.cpp create mode 100644 "Ispiti/2026-07-09/PR2_2026-07-09_G1_Rje\305\241enje.cpp" diff --git a/Ispiti/2026-07-09/PR2_2026-07-09_G1_Postavka.cpp b/Ispiti/2026-07-09/PR2_2026-07-09_G1_Postavka.cpp new file mode 100644 index 0000000..00db945 --- /dev/null +++ b/Ispiti/2026-07-09/PR2_2026-07-09_G1_Postavka.cpp @@ -0,0 +1,679 @@ +#include +using namespace std; + +string crt = "\n---------------------------------------------------------------------------------\n"; + +string PORUKA_TELEFON = crt + + "TELEFONE ISKLJUCITE I ODLOZITE U TORBU, DZEP ILI DRUGU LOKACIJU VAN " + + "DOHVATA.\n" + + "CESTO SE NA TELEFONIMA (PRO)NALAZE PROGRAMSKI KODOVI KOJI MOGU BITI " + + "ISKORISTENI ZA\n" + + "RJESAVANJE ISPITNOG ZADATKA, STO CE, U SLUCAJU PRONALASKA, BITI " + + "SANKCIONISANO.\n\n" + + "SVI KOD KOJIH BUDE PRONADJEN TELEFON ILI NEKI DRUGI UREDJAJ KOJI MOZE " + + "SADRZAVATI\n" + + "MATERIJAL ILI RJESENJA ISPITNIH ZADATAKA CE BITI UDALJENI SA ISPITA I " + + "BIT CE POKRENUT\n" + + "POSTUPAK PROTIV NJIH" + crt; + +string PORUKA_ISPIT = crt + + "0. PROVJERITE DA LI ZADACI PRIPADAJU VASOJ GRUPI (G1/G2)\n" + + "1. SVE KLASE SA DINAMICKOM ALOKACIJOM MORAJU IMATI ISPRAVAN " + + "DESTRUKTOR\n" + + "2. IZOSTAVLJANJE DESTRUKTORA ILI NJEGOVIH DIJELOVA BIT CE OZNACENO " + + "KAO TM\n" + + "3. ATRIBUTI, METODE I PARAMETRI MORAJU BITI IDENTICNI ONIMA U TESTNOJ " + + "MAIN FUNKCIJI,\n" + + " OSIM AKO POSTOJI JASNO OPISAN RAZLOG ZA MODIFIKACIJU\n" + + "4. IZUZETKE BACAJTE SAMO TAMO GDJE JE IZRICITO NAGLASENO\n" + + "5. SVE METODE KOJE SE POZIVAJU U MAIN-U MORAJU POSTOJATI.\n" + + " AKO NEMATE ZELJENU IMPLEMENTACIJU, OSTAVITE PRAZNO TIJELO ILI " + + "VRATITE DEFAULT VRIJEDNOST\n" + + "6. U MAIN FUNKCIJI MOZETE DODAVATI TESTNE PODATKE I POZIVE PO " + + "VLASTITOM IZBORU\n" + + "7. TESTIRAJTE PROGRAM U OBA MODA (F5 i Ctrl+F5)" + crt; + + +char* AlocirajTekst(const char* tekst) { + if (tekst == nullptr) + return nullptr; + + size_t velicina = strlen(tekst) + 1; + char* noviTekst = new char[velicina]; + + strcpy_s(noviTekst, velicina, tekst); + + return noviTekst; +} + + +enum VrstaDostave { + STANDARDNA, + BRZA, + MEDJUNARODNA +}; + +const char* VrstaDostaveOznake[] = { + "ST", + "BR", + "MD" +}; + +const char* VrstaDostaveNazivi[] = { + "STANDARDNA", + "BRZA", + "MEDJUNARODNA" +}; + + +enum StatusPosiljke { + KREIRANA, + PREUZETA, + U_TRANZITU, + ISPORUCENA, + OTKAZANA +}; + +const char* StatusPosiljkeNazivi[] = { + "KREIRANA", + "PREUZETA", + "U TRANZITU", + "ISPORUCENA", + "OTKAZANA" +}; + + +template +class Kolekcija { + T1* _elementi1[max]; + T2* _elementi2[max]; + int _trenutno; + +public: + Kolekcija() : _trenutno(0) { + for (int i = 0; i < max; i++) { + _elementi1[i] = nullptr; + _elementi2[i] = nullptr; + } + } + + int GetTrenutno() const { + return _trenutno; + } + + T1& GetElement1(int indeks) { + return *_elementi1[indeks]; + } + + T2& GetElement2(int indeks) { + return *_elementi2[indeks]; + } + + const T1& GetElement1(int indeks) const { + return *_elementi1[indeks]; + } + + const T2& GetElement2(int indeks) const { + return *_elementi2[indeks]; + } + + T1& operator[](int indeks) { + return *_elementi1[indeks]; + } + + friend ostream& operator<<(ostream& COUT, const Kolekcija& obj) { + for (int i = 0; i < obj.GetTrenutno(); i++) + COUT << obj.GetElement1(i) << " " + << obj.GetElement2(i) << endl; + + return COUT; + } + + ~Kolekcija() { + for (int i = 0; i < _trenutno; i++) { + delete _elementi1[i]; + delete _elementi2[i]; + + _elementi1[i] = nullptr; + _elementi2[i] = nullptr; + } + + _trenutno = 0; + } +}; + + +class DatumVrijeme { + int* _dan, * _mjesec, * _godina, * _sati, * _minute; + +public: + DatumVrijeme( + int dan = 1, + int mjesec = 1, + int godina = 2000, + int sati = 0, + int minute = 0) { + + _dan = new int(dan); + _mjesec = new int(mjesec); + _godina = new int(godina); + _sati = new int(sati); + _minute = new int(minute); + } + + friend ostream& operator<<(ostream& COUT, const DatumVrijeme& obj) { + //ToString vraca datum i vrijeme u formatu DD.MM.GGGG HH:MM, + COUT << obj.ToString(); + + return COUT; + } + + ~DatumVrijeme() { + delete _dan; + delete _mjesec; + delete _godina; + delete _sati; + delete _minute; + + _dan = _mjesec = _godina = _sati = _minute = nullptr; + } +}; + + +class Posiljka { + char* _oznaka; + char* _sadrzaj; + char* _imePrezimePrimaoca; + char* _emailPrimaoca; + VrstaDostave _vrstaDostave; + int _redniBroj; + + Kolekcija _statusi; + + void Dealociraj() { + delete[] _oznaka; + delete[] _sadrzaj; + delete[] _imePrezimePrimaoca; + delete[] _emailPrimaoca; + + _oznaka = _sadrzaj = _imePrezimePrimaoca = + _emailPrimaoca = nullptr; + } + +public: + const char* GetOznaka() const { + return _oznaka; + } + + const char* GetSadrzaj() const { + return _sadrzaj; + } + + const char* GetImePrezimePrimaoca() const { + return _imePrezimePrimaoca; + } + + const char* GetEmailPrimaoca() const { + return _emailPrimaoca; + } + + VrstaDostave GetVrstaDostave() const { + return _vrstaDostave; + } + + int GetRedniBroj() const { + return _redniBroj; + } + + Kolekcija& GetStatusi() { + return _statusi; + } + + StatusPosiljke GetTrenutniStatus() const { + return _statusi.GetElement1(_statusi.GetTrenutno() - 1); + } + + friend ostream& operator<<(ostream& COUT, const Posiljka& obj) { + // ToString vraca podatke u formatu: + // oznaka | ime i prezime | vrsta dostave | sadrzaj | trenutni status + // BH-BR-0042-AB | Amina Buric | BRZA | Dokumenti | KREIRANA + + COUT << obj.ToString(); + + return COUT; + } + + ~Posiljka() { + Dealociraj(); + } +}; + + +class DostavnaSluzba { + char* _naziv; + vector _posiljke; + +public: + DostavnaSluzba(const char* naziv = "") { + _naziv = AlocirajTekst(naziv); + } + + const char* GetNaziv() const { + return _naziv; + } + + vector& GetPosiljke() { + return _posiljke; + } + + ~DostavnaSluzba() { + delete[] _naziv; + _naziv = nullptr; + } +}; + + +const char* GetOdgovorNaPrvoPitanje() { + cout << "Pitanje -> Pojasnite da li se svako nasljedjivanje moze " + << "posmatrati kao polimorfizam i obrnuto? Navedite konkretne " + << "primjere.\n"; + + return "Odgovor -> OVDJE UNESITE VAS ODGOVOR"; +} + + +const char* GetOdgovorNaDrugoPitanje() { + cout << "Pitanje -> Pojasnite razloge kojima bi se implementator " + << "klase mogao voditi pri donosenju odluke da sve metode u klasi " + << "(ne)proglasi virtualnim?\n"; + + return "Odgovor -> OVDJE UNESITE VAS ODGOVOR"; +} + + +int main() { + cout << PORUKA_TELEFON; + cin.get(); + + cout << PORUKA_TELEFON; + cin.get(); + + cout << PORUKA_ISPIT; + cin.get(); + + system("cls"); + + cout << GetOdgovorNaPrvoPitanje() << crt; + cin.get(); + + cout << GetOdgovorNaDrugoPitanje() << crt; + cin.get(); + + + /* + Funkcija GenerisiOznaku generise oznaku posiljke u formatu: BH-VD-BBBB-IN + Potpis funkcije treba biti: + string GenerisiOznaku(const char* imePrezime, int redniBroj, + VrstaDostave vrstaDostave) + BH -> fiksna oznaka drzave, + VD -> vrsta dostave: ST (standardna), BR (brza), MD (medjunarodna), + BBBB -> redni broj posiljke popunjen nulama na slobodnim mjestima, + IN -> inicijali imena i prezimena primaoca. + Kod imena koje sadrzi vise rijeci koriste se inicijal prve i posljednje + rijeci. Redni broj mora biti u rasponu 1-9999. + Za neispravne podatke funkcija vraca "BH-XX-0000-XX". + */ + + if (GenerisiOznaku("Amina Buric", 42, BRZA) == "BH-BR-0042-AB") + cout << "Oznaka OK" << crt; + + if (GenerisiOznaku("Goran Skondric", 7, STANDARDNA) == + "BH-ST-0007-GS") + cout << "Oznaka OK" << crt; + + if (GenerisiOznaku("Ana Marija Kovac", 156, MEDJUNARODNA) == + "BH-MD-0156-AK") + cout << "Oznaka OK" << crt; + + if (GenerisiOznaku("Amina", 42, BRZA) == "BH-XX-0000-XX" && + GenerisiOznaku("Amina Buric", 0, BRZA) == "BH-XX-0000-XX" && + GenerisiOznaku("Amina Buric", 10000, BRZA) == "BH-XX-0000-XX" && + GenerisiOznaku("Amina Buric", 42, (VrstaDostave)99) == + "BH-XX-0000-XX") + cout << "Neispravni podaci za oznaku OK" << crt; + + + /* + Koristeci regex, funkcija ValidirajOznaku provjerava da li je oznaka + zapisana u prethodno definisanom formatu. Dozvoljene su iskljucivo oznake + vrsta ST, BR i MD, redni broj mora imati cetiri cifre i ne moze biti 0000, + a inicijali moraju biti zapisani velikim slovima. + Potpis funkcije treba biti: + bool ValidirajOznaku(const string& oznaka) + */ + + if (ValidirajOznaku("BH-BR-0042-AB")) + cout << "OZNAKA VALIDNA" << crt; + + if (!ValidirajOznaku("BH-EX-0042-AB") && + !ValidirajOznaku("BH-BR-042-AB") && + !ValidirajOznaku("BH-BR-0042-Ab") && + !ValidirajOznaku("BH-BR-0000-AB") && + !ValidirajOznaku("bh-BR-0042-AB")) + cout << "OZNAKA NIJE VALIDNA" << crt; + + if (ValidirajOznaku(GenerisiOznaku("Maid Ramic", 284, BRZA))) + cout << "GENERISANA OZNAKA VALIDNA" << crt; + + + Kolekcija brojevi; + + brojevi.Dodaj(10, "Deset"); + brojevi.Dodaj(20, "Dvadeset"); + brojevi.Dodaj(30, "Trideset"); + + cout << brojevi << crt; + + + /* + DodajNaPoziciju dodaje novi par na lokaciju/indeks definisanu + prvim parametrom, + pomjera postojece pokazivace udesno i vraca trenutno stanje + kolekcije (izmijenjeni tj. prosireni objekat). + U slucaju popunjene kolekcije ili neispravne lokacije potrebno je + baciti izuzetak. + */ + + Kolekcija prosireniBrojevi = + brojevi.DodajNaPoziciju(1, 15, "Petnaest"); + + cout << prosireniBrojevi << crt; + + + /* + UkloniSaPozicije uklanja par sa proslijedjene pozicije/indeksa, + preostale elemente + pomjera ulijevo i vraca uklonjeni par u obliku pair. + Za poziciju izvan opsega baca se izuzetak. + */ + + pair uklonjeni = + prosireniBrojevi.UkloniSaPozicije(2); + + cout << "Uklonjeno: " << uklonjeni.first << " " + << uklonjeni.second << crt; + + cout << "Preostali elementi:" << crt + << prosireniBrojevi; + + try { + prosireniBrojevi.UkloniSaPozicije(10); + } + catch (exception& e) { + cout << "Exception: " << e.what() << crt; + } + + + try { + Kolekcija punaKolekcija; + + punaKolekcija.Dodaj(1, "Jedan"); + punaKolekcija.Dodaj(2, "Dva"); + punaKolekcija.Dodaj(3, "Tri"); + + punaKolekcija.DodajNaPoziciju(1, 4, "Cetiri"); + } + catch (exception& e) { + cout << "Exception: " << e.what() << crt; + } + + + Kolekcija kopijaBrojeva = brojevi; + + kopijaBrojeva[0] = 100; + + Kolekcija dodijeljeniBrojevi; + + dodijeljeniBrojevi = brojevi; + + dodijeljeniBrojevi[1] = 200; + + cout << "Original:" << crt << brojevi; + cout << "Kopija:" << crt << kopijaBrojeva; + cout << "Dodijeljeni objekat:" << crt << dodijeljeniBrojevi; + + + DatumVrijeme kreirana(1, 7, 2026, 8, 0); + DatumVrijeme preuzeta(1, 7, 2026, 9, 0); + DatumVrijeme tranzit(1, 7, 2026, 10, 0); + DatumVrijeme isporucena(1, 7, 2026, 11, 0); + + + /* + ToString vraca datum i vrijeme u formatu DD.MM.GGGG HH:MM, + ukljucujuci + pocetne nule. + */ + + cout << kreirana.ToString() << crt; //01.07.2026 08:00 + + if (preuzeta > kreirana) + cout << "Vrijeme preuzimanja je nakon vremena kreiranja" << crt; + + DatumVrijeme kopijaPreuzeta(preuzeta); + + if (kopijaPreuzeta == preuzeta && !(kreirana == preuzeta)) + cout << "Provjera vremena, OK." << crt; + + + /* + Konstruktor Posiljke generise oznaku i evidentira pocetni status + KREIRANA + sa proslijedjenim vremenom kreiranja. + */ + + Posiljka dokumenti( + "Dokumenti", + "Amina Buric", + "amina@fit.ba", + BRZA, + 42, + kreirana + ); + + Posiljka knjige( + "Knjige", + "Goran Skondric", + "goran@fit.ba", + STANDARDNA, + 7, + kreirana + ); + + Posiljka oprema( + "Oprema", + "Ana Marija Kovac", + "ana@fit.ba", + MEDJUNARODNA, + 156, + kreirana + ); + + + /* + ToString vraca podatke u formatu: + oznaka | ime i prezime | vrsta dostave | sadrzaj | trenutni status + BH-BR-0042-AB | Amina Buric | BRZA | Dokumenti | KREIRANA + */ + + cout << dokumenti.ToString() << crt; + + if (dokumenti.ToString() == + "BH-BR-0042-AB | Amina Buric | BRZA | Dokumenti | KREIRANA") + cout << "Posiljka ToString OK" << crt; + + + /* + DodajStatus dodaje status samo ako je vrijeme vece od vremena + posljednjeg statusa i ako je promjena statusa dozvoljena. + Dozvoljen slijed statusa je: KREIRANA -> PREUZETA -> U_TRANZITU -> + ISPORUCENA + OTKAZANA se moze evidentirati iz bilo kojeg statusa koji nije zavrsni. + Nakon statusa ISPORUCENA ili OTKAZANA nisu dozvoljene nove promjene. + Preskakanje, ponavljanje statusa i hronoloski neispravno vrijeme vracaju + false, bez izmjene kolekcije statusa. + */ + + if (dokumenti.DodajStatus(PREUZETA, preuzeta)) + cout << "Status PREUZETA dodan" << crt; + + if (dokumenti.DodajStatus(U_TRANZITU, tranzit)) + cout << "Status U TRANZITU dodan" << crt; + + if (dokumenti.DodajStatus(ISPORUCENA, isporucena)) + cout << "Status ISPORUCENA dodan" << crt; + + if (!dokumenti.DodajStatus(OTKAZANA, isporucena)) + cout << "Promjena zavrsnog statusa nije dozvoljena" << crt; + + if (!knjige.DodajStatus(U_TRANZITU, preuzeta)) + cout << "Preskakanje statusa nije dozvoljeno" << crt; + + if (oprema.DodajStatus(OTKAZANA, preuzeta)) + cout << "Posiljka otkazana" << crt; + + + Posiljka kopijaDokumenata = dokumenti; + + cout << kopijaDokumenata << crt; + + + DostavnaSluzba brzaPosta("Brza posta"); + + + /* + DodajPosiljku dodaje posiljku u dostavnu sluzbu. Nije dozvoljeno dodati + dvije posiljke sa istim rednim brojem ili istom oznakom. U slucaju + duplikata metoda baca izuzetak. + */ + + brzaPosta.DodajPosiljku(knjige); + brzaPosta.DodajPosiljku(oprema); + + try { + brzaPosta.DodajPosiljku(knjige); + } + catch (exception& e) { + cout << "Exception: " << e.what() << crt; + } + + + /* + PronadjiPosiljku vraca pokazivac na posiljku sa proslijedjenom oznakom. + Ako posiljka nije pronadjena, metoda vraca nullptr. + */ + + string oznakaKnjige = knjige.GetOznaka(); + + Posiljka* pronadjena = + brzaPosta.PronadjiPosiljku(oznakaKnjige); + + if (pronadjena != nullptr) + cout << "Pronadjena posiljka: " + << pronadjena->GetOznaka() << crt; + + if (brzaPosta.PronadjiPosiljku("BH-ST-9999-XX") == nullptr) + cout << "Nepostojeca posiljka nije pronadjena" << crt; + + + /* + EvidentirajStatus pronalazi posiljku na osnovu oznake i pokusava + dodati + novi status koristeci pravila metode DodajStatus. + Nakon svakog uspjesno evidentiranog statusa potrebno je u zasebnom + threadu + poslati obavijest primaocu. Obavijest se ne salje ako posiljka ne + postoji ili status nije dodan, te u tim slucajevima metoda vraca + false. + Primjer sadrzaja obavijesti: + ------------------------------------------------------------------ + --------------- + To: goran@fit.ba + From: info@brzaposta.ba + Subject: Promjena statusa posiljke + Postovani Goran Skondric, + Posiljka BH-ST-0007-GS je promijenila status u PREUZETA. + Vrijeme evidentiranja: 01.07.2026 09:00 + Hvala sto koristite nase usluge. + BrzaPosta Tim + ------------------------------------------------------------------ + --------------- + */ + + if (brzaPosta.EvidentirajStatus( + oznakaKnjige, + PREUZETA, + preuzeta)) + cout << "Status evidentiran i obavijest poslana" << crt; + + if (!brzaPosta.EvidentirajStatus( + oznakaKnjige, + ISPORUCENA, + isporucena)) + cout << "Obavijest nije poslana za neispravnu promjenu " + << "statusa" << crt; + + if (!brzaPosta.EvidentirajStatus( + "NEPOSTOJECA", + PREUZETA, + preuzeta)) + cout << "Obavijest nije poslana za nepostojecu posiljku" + << crt; + + + /* + PosiljkePoStatusu vraca vector pokazivaca na sve posiljke ciji trenutni + status odgovara proslijedjenom statusu. + */ + + vector preuzetePosiljke = + brzaPosta.PosiljkePoStatusu(PREUZETA); + + for (auto posiljka : preuzetePosiljke) + cout << posiljka->GetOznaka() << " -> " + << StatusPosiljkeNazivi[ + (int)posiljka->GetTrenutniStatus() + ] + << crt; + + + /* + StatistikaPoStatusu vraca kolekciju koja za svaki status, + redoslijedom + definisanim enumom StatusPosiljke, sadrzi status i broj posiljki koje se + trenutno nalaze u tom statusu. U rezultat se dodaje svih pet statusa, + ukljucujuci i one za koje je broj posiljki jednak nuli. + */ + + Kolekcija statistika = + brzaPosta.StatistikaPoStatusu(); + + for (int i = 0; i < statistika.GetTrenutno(); i++) + cout << StatusPosiljkeNazivi[ + (int)statistika.GetElement1(i) + ] + << " -> " + << statistika.GetElement2(i) + << crt; + + + DostavnaSluzba kopijaSluzbe = brzaPosta; + + cout << kopijaSluzbe.GetNaziv() << " ima " + << kopijaSluzbe.GetPosiljke().size() + << " posiljki" << crt; + + cin.get(); + + return 0; +} diff --git "a/Ispiti/2026-07-09/PR2_2026-07-09_G1_Rje\305\241enje.cpp" "b/Ispiti/2026-07-09/PR2_2026-07-09_G1_Rje\305\241enje.cpp" new file mode 100644 index 0000000..e633287 --- /dev/null +++ "b/Ispiti/2026-07-09/PR2_2026-07-09_G1_Rje\305\241enje.cpp" @@ -0,0 +1,1020 @@ +// vela havle et + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +string crt = "\n---------------------------------------------------------------------------------\n"; + +string PORUKA_TELEFON = crt + +"TELEFONE ISKLJUCITE I ODLOZITE U TORBU, DZEP ILI DRUGU LOKACIJU VAN " + +"DOHVATA.\n" + +"CESTO SE NA TELEFONIMA (PRO)NALAZE PROGRAMSKI KODOVI KOJI MOGU BITI " + +"ISKORISTENI ZA\n" + +"RJESAVANJE ISPITNOG ZADATKA, STO CE, U SLUCAJU PRONALASKA, BITI " + +"SANKCIONISANO.\n\n" + +"SVI KOD KOJIH BUDE PRONADJEN TELEFON ILI NEKI DRUGI UREDJAJ KOJI MOZE " + +"SADRZAVATI\n" + +"MATERIJAL ILI RJESENJA ISPITNIH ZADATAKA CE BITI UDALJENI SA ISPITA I " + +"BIT CE POKRENUT\n" + +"POSTUPAK PROTIV NJIH" + crt; + +string PORUKA_ISPIT = crt + +"0. PROVJERITE DA LI ZADACI PRIPADAJU VASOJ GRUPI (G1/G2)\n" + +"1. SVE KLASE SA DINAMICKOM ALOKACIJOM MORAJU IMATI ISPRAVAN " + +"DESTRUKTOR\n" + +"2. IZOSTAVLJANJE DESTRUKTORA ILI NJEGOVIH DIJELOVA BIT CE OZNACENO " + +"KAO TM\n" + +"3. ATRIBUTI, METODE I PARAMETRI MORAJU BITI IDENTICNI ONIMA U TESTNOJ " + +"MAIN FUNKCIJI,\n" + +" OSIM AKO POSTOJI JASNO OPISAN RAZLOG ZA MODIFIKACIJU\n" + +"4. IZUZETKE BACAJTE SAMO TAMO GDJE JE IZRICITO NAGLASENO\n" + +"5. SVE METODE KOJE SE POZIVAJU U MAIN-U MORAJU POSTOJATI.\n" + +" AKO NEMATE ZELJENU IMPLEMENTACIJU, OSTAVITE PRAZNO TIJELO ILI " + +"VRATITE DEFAULT VRIJEDNOST\n" + +"6. U MAIN FUNKCIJI MOZETE DODAVATI TESTNE PODATKE I POZIVE PO " + +"VLASTITOM IZBORU\n" + +"7. TESTIRAJTE PROGRAM U OBA MODA (F5 i Ctrl+F5)" + crt; + + +char* AlocirajTekst(const char* tekst) { + if (tekst == nullptr) + return nullptr; + + size_t velicina = strlen(tekst) + 1; + char* noviTekst = new char[velicina]; + + strcpy_s(noviTekst, velicina, tekst); + + return noviTekst; +} + + + +enum VrstaDostave { + STANDARDNA, + BRZA, + MEDJUNARODNA +}; + +const char* VrstaDostaveOznake[] = { + "ST", + "BR", + "MD" +}; + +const char* VrstaDostaveNazivi[] = { + "STANDARDNA", + "BRZA", + "MEDJUNARODNA" +}; + + +enum StatusPosiljke { + KREIRANA, + PREUZETA, + U_TRANZITU, + ISPORUCENA, + OTKAZANA +}; + +const char* StatusPosiljkeNazivi[] = { + "KREIRANA", + "PREUZETA", + "U TRANZITU", + "ISPORUCENA", + "OTKAZANA" +}; + + +string GenerisiOznaku(const char* imePrezime, int redniBroj, VrstaDostave vrstaDostave) { + if (redniBroj < 1 || redniBroj > 9999) return "BH-XX-0000-XX"; + if(vrstaDostave MEDJUNARODNA) return "BH-XX-0000-XX"; + + std::ostringstream buffer; + string s_imePrezime = imePrezime; + size_t lastSpaceIndex = s_imePrezime.find_last_of(' '); + + // find_last_of() returna string::npos ako nije proslo || gleda dal je zadnji space jednak zadnjem karakteru u stringu + if (lastSpaceIndex == string::npos || lastSpaceIndex == s_imePrezime.size() - 1) { + return "BH-XX-0000-XX"; + } + + buffer << "BH-" << VrstaDostaveOznake[vrstaDostave] << "-" << setfill('0') << setw(4) << redniBroj << "-" << imePrezime[0] << imePrezime[lastSpaceIndex + 1]; + + return buffer.str(); +} + +bool ValidirajOznaku(const string& oznaka) { + return regex_match(oznaka, regex("BH-(ST|BR|MD)-(?!0000)[0-9]{4}-([A-Z]{2})")); +} + + +template +class Kolekcija { + T1* _elementi1[max]; + T2* _elementi2[max]; + int _trenutno; + +public: + Kolekcija() : _trenutno(0) { + for (int i = 0; i < max; i++) { + _elementi1[i] = nullptr; + _elementi2[i] = nullptr; + } + } + + Kolekcija(const Kolekcija& obj) { + // ovo radim jer _trenutno moze biti 3, a max 10 npr tako da se sve mora inicijalizirati, ako ne budes to uradio sjebat ces destruktor jer ce pokusat obrisat nesto sto ne postoji uopste + for (int i = 0; i < max; i++) { + _elementi1[i] = nullptr; + _elementi2[i] = nullptr; + } + + _trenutno = obj._trenutno; + for (int i = 0; i < _trenutno; i++) { + _elementi1[i] = new T1(*obj._elementi1[i]); + _elementi2[i] = new T2(*obj._elementi2[i]); + } + } + + Kolekcija& operator=(const Kolekcija& obj) { + if (this == &obj) return *this; + for (int i = 0; i < _trenutno; i++) { + delete _elementi1[i]; + delete _elementi2[i]; + + _elementi1[i] = nullptr; + _elementi2[i] = nullptr; + } + + _trenutno = obj._trenutno; + + for (int i = 0; i < _trenutno; i++) { + _elementi1[i] = new T1(*obj._elementi1[i]); + _elementi2[i] = new T2(*obj._elementi2[i]); + } + + return *this; + } + + + int GetTrenutno() const { + return _trenutno; + } + + T1& GetElement1(int indeks) { + return *_elementi1[indeks]; + } + + T2& GetElement2(int indeks) { + return *_elementi2[indeks]; + } + + const T1& GetElement1(int indeks) const { + return *_elementi1[indeks]; + } + + const T2& GetElement2(int indeks) const { + return *_elementi2[indeks]; + } + + T1& operator[](int indeks) { + return *_elementi1[indeks]; + } + + friend ostream& operator<<(ostream& COUT, const Kolekcija& obj) { + for (int i = 0; i < obj.GetTrenutno(); i++) + COUT << obj.GetElement1(i) << " " + << obj.GetElement2(i) << endl; + + return COUT; + } + + ~Kolekcija() { + for (int i = 0; i < _trenutno; i++) { + delete _elementi1[i]; + delete _elementi2[i]; + + _elementi1[i] = nullptr; + _elementi2[i] = nullptr; + } + + _trenutno = 0; + } + + void Dodaj(T1 el1, T2 el2) { + if (_trenutno >= max) return; + _elementi1[_trenutno] = new T1(el1); + _elementi2[_trenutno] = new T2(el2); + + _trenutno++; + } + + + Kolekcija& DodajNaPoziciju(int pos, T1 el1, T2 el2) { + + if (_trenutno >= max || pos < 0 || pos > _trenutno) { + throw exception("invalid arguments"); + } + + for (int i = _trenutno; i > pos; i--) { + _elementi1[i] = _elementi1[i - 1]; + _elementi2[i] = _elementi2[i - 1]; + } + + _elementi1[pos] = new T1(el1); + _elementi2[pos] = new T2(el2); + _trenutno++; + + return *this; + } + + + pair UkloniSaPozicije(int pos) { + pair par; + + if (pos < 0 || pos >= _trenutno) + throw exception("invalid pos"); + + par.first = *_elementi1[pos]; + par.second = *_elementi2[pos]; + + delete _elementi1[pos]; + delete _elementi2[pos]; + + for (int i = pos; i < _trenutno - 1; i++) { + _elementi1[i] = _elementi1[i + 1]; + _elementi2[i] = _elementi2[i + 1]; + } + + + _elementi1[_trenutno - 1] = nullptr; + _elementi2[_trenutno - 1] = nullptr; + _trenutno--; + + + return par; + } +}; + + +class DatumVrijeme { + int* _dan, * _mjesec, * _godina, * _sati, * _minute; + +public: + DatumVrijeme( + int dan = 1, + int mjesec = 1, + int godina = 2000, + int sati = 0, + int minute = 0) { + + _dan = new int(dan); + _mjesec = new int(mjesec); + _godina = new int(godina); + _sati = new int(sati); + _minute = new int(minute); + } + + + DatumVrijeme(const DatumVrijeme& obj) { + _dan = new int(*obj._dan); + _mjesec = new int(*obj._mjesec); + _godina = new int(*obj._godina); + _sati = new int(*obj._sati); + _minute = new int(*obj._minute); + } + + DatumVrijeme& operator=(const DatumVrijeme& obj) { + if (this == &obj) return *this; + delete _dan; + delete _mjesec; + delete _godina; + delete _sati; + delete _minute; + + _dan = _mjesec = _godina = _sati = _minute = nullptr; + + _dan = new int(*obj._dan); + _mjesec = new int(*obj._mjesec); + _godina = new int(*obj._godina); + _sati = new int(*obj._sati); + _minute = new int(*obj._minute); + return *this; + } + + friend ostream& operator<<(ostream& COUT, const DatumVrijeme& obj) { + //ToString vraca datum i vrijeme u formatu DD.MM.GGGG HH:MM, + COUT << obj.ToString(); + + return COUT; + } + + ~DatumVrijeme() { + delete _dan; + delete _mjesec; + delete _godina; + delete _sati; + delete _minute; + + _dan = _mjesec = _godina = _sati = _minute = nullptr; + } + + + string ToString() const { + ostringstream buffer; + + buffer << setfill('0') << setw(2) << *_dan << "."; + buffer << setfill('0') << setw(2) << *_mjesec << "."; + buffer << setfill('0') << setw(4) << *_godina << " "; + + buffer << setfill('0') << setw(2) << *_sati << ":"; + buffer << setfill('0') << setw(2) << *_minute; + + + return buffer.str(); + } + + bool operator>(const DatumVrijeme& obj) const { + if (*_godina != *obj._godina) + return *_godina > *obj._godina; + + if (*_mjesec != *obj._mjesec) + return *_mjesec > *obj._mjesec; + + if (*_dan != *obj._dan) + return *_dan > *obj._dan; + + if (*_sati != *obj._sati) + return *_sati > *obj._sati; + + return *_minute > *obj._minute; + } + + bool operator==(const DatumVrijeme& obj) const { + return (*_godina == *obj._godina && *_mjesec == *obj._mjesec && *_dan == *obj._dan && *_sati == *obj._sati && *_minute == *obj._minute); + } + +}; + + +class Posiljka { + char* _oznaka; + char* _sadrzaj; + char* _imePrezimePrimaoca; + char* _emailPrimaoca; + VrstaDostave _vrstaDostave; + int _redniBroj; + + Kolekcija _statusi; + + void Dealociraj() { + delete[] _oznaka; + delete[] _sadrzaj; + delete[] _imePrezimePrimaoca; + delete[] _emailPrimaoca; + + _oznaka = _sadrzaj = _imePrezimePrimaoca = + _emailPrimaoca = nullptr; + } + +public: + Posiljka(const char* sadrzaj, const char* imePrezimePrimaoca, const char* emailPrimaoca, VrstaDostave vrstaDostave, int redniBroj, const DatumVrijeme& vrijeme) { + _oznaka = AlocirajTekst(GenerisiOznaku(imePrezimePrimaoca, redniBroj, vrstaDostave).c_str()); + _sadrzaj = AlocirajTekst(sadrzaj); + _imePrezimePrimaoca = AlocirajTekst(imePrezimePrimaoca); + _emailPrimaoca = AlocirajTekst(emailPrimaoca); + _vrstaDostave = vrstaDostave; + _redniBroj = redniBroj; + + _statusi.Dodaj(KREIRANA, vrijeme); + } + + Posiljka(const Posiljka& obj) { + _oznaka = AlocirajTekst(obj.GetOznaka()); + _sadrzaj = AlocirajTekst(obj.GetSadrzaj()); + _imePrezimePrimaoca = AlocirajTekst(obj.GetImePrezimePrimaoca()); + _emailPrimaoca = AlocirajTekst(obj.GetEmailPrimaoca()); + _vrstaDostave = obj.GetVrstaDostave(); + _redniBroj = obj.GetRedniBroj(); + + _statusi = obj._statusi; + } + + Posiljka& operator=(const Posiljka& obj) { + if (this == &obj) return *this; + + Dealociraj(); + _oznaka = AlocirajTekst(obj.GetOznaka()); + _sadrzaj = AlocirajTekst(obj.GetSadrzaj()); + _imePrezimePrimaoca = AlocirajTekst(obj.GetImePrezimePrimaoca()); + _emailPrimaoca = AlocirajTekst(obj.GetEmailPrimaoca()); + _vrstaDostave = obj.GetVrstaDostave(); + _redniBroj = obj.GetRedniBroj(); + + _statusi = obj._statusi; + + return *this; + } + + const char* GetOznaka() const { + return _oznaka; + } + + const char* GetSadrzaj() const { + return _sadrzaj; + } + + const char* GetImePrezimePrimaoca() const { + return _imePrezimePrimaoca; + } + + const char* GetEmailPrimaoca() const { + return _emailPrimaoca; + } + + VrstaDostave GetVrstaDostave() const { + return _vrstaDostave; + } + + int GetRedniBroj() const { + return _redniBroj; + } + + Kolekcija& GetStatusi() { + return _statusi; + } + + StatusPosiljke GetTrenutniStatus() const { + return _statusi.GetElement1(_statusi.GetTrenutno() - 1); + } + + friend ostream& operator<<(ostream& COUT, const Posiljka& obj) { + // ToString vraca podatke u formatu: + // oznaka | ime i prezime | vrsta dostave | sadrzaj | trenutni status + // BH-BR-0042-AB | Amina Buric | BRZA | Dokumenti | KREIRANA + + COUT << obj.ToString(); + + return COUT; + } + + ~Posiljka() { + Dealociraj(); + } + + string ToString() const { + ostringstream buffer; + + buffer << _oznaka << " | " << _imePrezimePrimaoca << " | " << VrstaDostaveNazivi[_vrstaDostave] << " | " << _sadrzaj << " | " << StatusPosiljkeNazivi[GetTrenutniStatus()]; + + return buffer.str(); + } + + bool DodajStatus(StatusPosiljke status, const DatumVrijeme& vrijeme) { + DatumVrijeme posljednjeVrijeme = _statusi.GetElement2(_statusi.GetTrenutno() - 1); + StatusPosiljke posljednjiStatus = _statusi.GetElement1(_statusi.GetTrenutno() - 1); + if (vrijeme > posljednjeVrijeme) { + if (status == OTKAZANA) { + if (posljednjiStatus == OTKAZANA || posljednjiStatus == ISPORUCENA) { + return false; + } + _statusi.Dodaj(status, vrijeme); + return true; + } + // posto su poredani u enumu onako kako trebaju gledam jesu li tacno jedan iza drugog + else if ((int)status == (int)(posljednjiStatus + 1)) { + _statusi.Dodaj(status, vrijeme); + return true; + } + } + + return false; + } +}; + + +class DostavnaSluzba { + char* _naziv; + vector _posiljke; + +public: + DostavnaSluzba(const char* naziv = "") { + _naziv = AlocirajTekst(naziv); + } + + const char* GetNaziv() const { + return _naziv; + } + + vector& GetPosiljke() { + return _posiljke; + } + + ~DostavnaSluzba() { + delete[] _naziv; + _naziv = nullptr; + } + + + + + bool PosiljkaExists(int redniBroj) { + return any_of(_posiljke.begin(), _posiljke.end(), [&](const Posiljka &p) { + return p.GetRedniBroj() == redniBroj; + }); + } + bool PosiljkaExists(const char* oznaka) { + return any_of(_posiljke.begin(), _posiljke.end(), [&](const Posiljka& p) { + return strcmp(oznaka, p.GetOznaka()) == 0; + }); + } + + + void DodajPosiljku(const Posiljka& posiljka) { + if (PosiljkaExists(posiljka.GetRedniBroj())) { + throw exception("already exists"); + } + + if (PosiljkaExists(posiljka.GetOznaka())) { + throw exception("already exists"); + } + + _posiljke.push_back(posiljka); + + } + + Posiljka* PronadjiPosiljku(string oznaka) { + for (Posiljka& p : _posiljke) { + if (strcmp(oznaka.c_str(), p.GetOznaka()) == 0) { + return &p; + } + } + return nullptr; + } + + bool EvidentirajStatus(string oznaka, StatusPosiljke status, const DatumVrijeme& vrijeme) { + Posiljka* posiljka = PronadjiPosiljku(oznaka); + + if (!posiljka) return false; + + if (posiljka->DodajStatus(status, vrijeme)) { + + thread t([&]() { + ostringstream buffer; + + buffer << "---------------------------------------------------------------------------------" << endl << "To: " << posiljka->GetEmailPrimaoca() << endl << "From: info@brzaposta.ba" << endl; + buffer << "Subject: Promjena statusa posiljke" << endl << "Postovani " << posiljka->GetImePrezimePrimaoca() << "," << endl; + buffer << "Posiljka " << posiljka->GetOznaka() << "je promijenila status u " << StatusPosiljkeNazivi[status] << "." << endl; + buffer << "Vrijeme evidentiranja: " << vrijeme << endl << "Hvala sto koristite nase usluge.\nBrzaPosta Tim" << endl; + buffer << "---------------------------------------------------------------------------------\n"; + + cout << buffer.str(); + }); + t.join(); + return true; + } + return false; + } + vector PosiljkePoStatusu(StatusPosiljke status) { + vector posiljke; + + for (Posiljka& p : _posiljke) { + if (p.GetTrenutniStatus() == status) { + posiljke.push_back(&p); + } + } + + return posiljke; + } + + Kolekcija StatistikaPoStatusu() { + Kolekcija stats; + + for (int i = 0; i < 5; i++) { + StatusPosiljke status = (StatusPosiljke)i; + + int broj = 0; + + for (const Posiljka& p : _posiljke) { + if (p.GetTrenutniStatus() == status) + broj++; + } + + stats.Dodaj(status, broj); + } + return stats; + } +}; + +const char* GetOdgovorNaPrvoPitanje() { + cout << "Pitanje -> Pojasnite da li se svako nasljedjivanje moze " + << "posmatrati kao polimorfizam i obrnuto? Navedite konkretne " + << "primjere.\n"; + + return "Odgovor -> Ne. Nasljedjivanje je odnos bazne i izvedene klase, dok polimorfizam omogucava da se preko bazne klase pozove metoda izvedene klase. Primjer: virtualna metoda."; +} + + +const char* GetOdgovorNaDrugoPitanje() { + cout << "Pitanje -> Pojasnite razloge kojima bi se implementator " + << "klase mogao voditi pri donosenju odluke da sve metode u klasi " + << "(ne)proglasi virtualnim?\n"; + + return "Odgovor -> Metode se proglasavaju virtualnim kada zelimo polimorfno ponasanje i redefinisanje metode u izvedenim klasama. Ne moraju sve metode biti virtualne jer neke nema potrebe redefinisati."; +} + + +int main() { + cout << PORUKA_TELEFON; + cin.get(); + + cout << PORUKA_TELEFON; + cin.get(); + + cout << PORUKA_ISPIT; + cin.get(); + + system("cls"); + + cout << GetOdgovorNaPrvoPitanje() << crt; + cin.get(); + + cout << GetOdgovorNaDrugoPitanje() << crt; + cin.get(); + + + /* + Funkcija GenerisiOznaku generise oznaku posiljke u formatu: BH-VD-BBBB-IN + Potpis funkcije treba biti: + string GenerisiOznaku(const char* imePrezime, int redniBroj, + VrstaDostave vrstaDostave) + BH -> fiksna oznaka drzave, + VD -> vrsta dostave: ST (standardna), BR (brza), MD (medjunarodna), + BBBB -> redni broj posiljke popunjen nulama na slobodnim mjestima, + IN -> inicijali imena i prezimena primaoca. + Kod imena koje sadrzi vise rijeci koriste se inicijal prve i posljednje + rijeci. Redni broj mora biti u rasponu 1-9999. + Za neispravne podatke funkcija vraca "BH-XX-0000-XX". + */ + + if (GenerisiOznaku("Amina Buric", 42, BRZA) == "BH-BR-0042-AB") + cout << "Oznaka OK" << crt; + + if (GenerisiOznaku("Goran Skondric", 7, STANDARDNA) == + "BH-ST-0007-GS") + cout << "Oznaka OK" << crt; + + if (GenerisiOznaku("Ana Marija Kovac", 156, MEDJUNARODNA) == + "BH-MD-0156-AK") + cout << "Oznaka OK" << crt; + + if (GenerisiOznaku("Amina", 42, BRZA) == "BH-XX-0000-XX" && + GenerisiOznaku("Amina Buric", 0, BRZA) == "BH-XX-0000-XX" && + GenerisiOznaku("Amina Buric", 10000, BRZA) == "BH-XX-0000-XX" && + GenerisiOznaku("Amina Buric", 42, (VrstaDostave)99) == + "BH-XX-0000-XX") + cout << "Neispravni podaci za oznaku OK" << crt; + + + /* + Koristeci regex, funkcija ValidirajOznaku provjerava da li je oznaka + zapisana u prethodno definisanom formatu. Dozvoljene su iskljucivo oznake + vrsta ST, BR i MD, redni broj mora imati cetiri cifre i ne moze biti 0000, + a inicijali moraju biti zapisani velikim slovima. + Potpis funkcije treba biti: + bool ValidirajOznaku(const string& oznaka) + */ + + if (ValidirajOznaku("BH-BR-0042-AB")) + cout << "OZNAKA VALIDNA" << crt; + + if (!ValidirajOznaku("BH-EX-0042-AB") && + !ValidirajOznaku("BH-BR-042-AB") && + !ValidirajOznaku("BH-BR-0042-Ab") && + !ValidirajOznaku("BH-BR-0000-AB") && + !ValidirajOznaku("bh-BR-0042-AB")) + cout << "OZNAKA NIJE VALIDNA" << crt; + + if (ValidirajOznaku(GenerisiOznaku("Maid Ramic", 284, BRZA))) + cout << "GENERISANA OZNAKA VALIDNA" << crt; + + + Kolekcija brojevi; + + brojevi.Dodaj(10, "Deset"); + brojevi.Dodaj(20, "Dvadeset"); + brojevi.Dodaj(30, "Trideset"); + + cout << brojevi << crt; + + + /* + DodajNaPoziciju dodaje novi par na lokaciju/indeks definisanu + prvim parametrom, + pomjera postojece pokazivace udesno i vraca trenutno stanje + kolekcije (izmijenjeni tj. prosireni objekat). + U slucaju popunjene kolekcije ili neispravne lokacije potrebno je + baciti izuzetak. + */ + + Kolekcija prosireniBrojevi = + brojevi.DodajNaPoziciju(1, 15, "Petnaest"); + + cout << prosireniBrojevi << crt; + + /* + + UkloniSaPozicije uklanja par sa proslijedjene pozicije/indeksa, + preostale elemente + pomjera ulijevo i vraca uklonjeni par u obliku pair. + Za poziciju izvan opsega baca se izuzetak. + */ + pair uklonjeni = + prosireniBrojevi.UkloniSaPozicije(2); + + cout << "Uklonjeno: " << uklonjeni.first << " " + << uklonjeni.second << crt; + + cout << "Preostali elementi:" << crt + << prosireniBrojevi; + + try { + prosireniBrojevi.UkloniSaPozicije(10); + } + catch (exception& e) { + cout << "Exception: " << e.what() << crt; + } + + + try { + Kolekcija punaKolekcija; + + punaKolekcija.Dodaj(1, "Jedan"); + punaKolekcija.Dodaj(2, "Dva"); + punaKolekcija.Dodaj(3, "Tri"); + + punaKolekcija.DodajNaPoziciju(1, 4, "Cetiri"); + } + catch (exception& e) { + cout << "Exception: " << e.what() << crt; + } + + + Kolekcija kopijaBrojeva = brojevi; + + kopijaBrojeva[0] = 100; + + Kolekcija dodijeljeniBrojevi; + + dodijeljeniBrojevi = brojevi; + + dodijeljeniBrojevi[1] = 200; + + cout << "Original:" << crt << brojevi; + cout << "Kopija:" << crt << kopijaBrojeva; + cout << "Dodijeljeni objekat:" << crt << dodijeljeniBrojevi; + + + + DatumVrijeme kreirana(1, 7, 2026, 8, 0); + DatumVrijeme preuzeta(1, 7, 2026, 9, 0); + DatumVrijeme tranzit(1, 7, 2026, 10, 0); + DatumVrijeme isporucena(1, 7, 2026, 11, 0); + + + /* + ToString vraca datum i vrijeme u formatu DD.MM.GGGG HH:MM, + ukljucujuci + pocetne nule. + + */ + cout << kreirana.ToString() << crt; //01.07.2026 08:00 + + if (preuzeta > kreirana) + cout << "Vrijeme preuzimanja je nakon vremena kreiranja" << crt; + + DatumVrijeme kopijaPreuzeta(preuzeta); + + if (kopijaPreuzeta == preuzeta && !(kreirana == preuzeta)) + cout << "Provjera vremena, OK." << crt; + + /* + + Konstruktor Posiljke generise oznaku i evidentira pocetni status + KREIRANA + sa proslijedjenim vremenom kreiranja. + */ + + Posiljka dokumenti( + "Dokumenti", + "Amina Buric", + "amina@fit.ba", + BRZA, + 42, + kreirana + ); + + Posiljka knjige( + "Knjige", + "Goran Skondric", + "goran@fit.ba", + STANDARDNA, + 7, + kreirana + ); + + Posiljka oprema( + "Oprema", + "Ana Marija Kovac", + "ana@fit.ba", + MEDJUNARODNA, + 156, + kreirana + ); + + + /* + ToString vraca podatke u formatu: + oznaka | ime i prezime | vrsta dostave | sadrzaj | trenutni status + BH-BR-0042-AB | Amina Buric | BRZA | Dokumenti | KREIRANA + */ + + cout << dokumenti.ToString() << crt; + + if (dokumenti.ToString() == + "BH-BR-0042-AB | Amina Buric | BRZA | Dokumenti | KREIRANA") + cout << "Posiljka ToString OK" << crt; + + + /* + DodajStatus dodaje status samo ako je vrijeme vece od vremena + posljednjeg statusa i ako je promjena statusa dozvoljena. + Dozvoljen slijed statusa je: KREIRANA -> PREUZETA -> U_TRANZITU -> + ISPORUCENA + OTKAZANA se moze evidentirati iz bilo kojeg statusa koji nije zavrsni. + Nakon statusa ISPORUCENA ili OTKAZANA nisu dozvoljene nove promjene. + Preskakanje, ponavljanje statusa i hronoloski neispravno vrijeme vracaju + false, bez izmjene kolekcije statusa. + */ + + if (dokumenti.DodajStatus(PREUZETA, preuzeta)) + cout << "Status PREUZETA dodan" << crt; + + if (dokumenti.DodajStatus(U_TRANZITU, tranzit)) + cout << "Status U TRANZITU dodan" << crt; + + if (dokumenti.DodajStatus(ISPORUCENA, isporucena)) + cout << "Status ISPORUCENA dodan" << crt; + + if (!dokumenti.DodajStatus(OTKAZANA, isporucena)) + cout << "Promjena zavrsnog statusa nije dozvoljena" << crt; + + if (!knjige.DodajStatus(U_TRANZITU, preuzeta)) + cout << "Preskakanje statusa nije dozvoljeno" << crt; + + if (oprema.DodajStatus(OTKAZANA, preuzeta)) + cout << "Posiljka otkazana" << crt; + + + Posiljka kopijaDokumenata = dokumenti; + + cout << kopijaDokumenata << crt; + + + DostavnaSluzba brzaPosta("Brza posta"); + + + /* + DodajPosiljku dodaje posiljku u dostavnu sluzbu. Nije dozvoljeno dodati + dvije posiljke sa istim rednim brojem ili istom oznakom. U slucaju + duplikata metoda baca izuzetak. + */ + + brzaPosta.DodajPosiljku(knjige); + brzaPosta.DodajPosiljku(oprema); + + try { + brzaPosta.DodajPosiljku(knjige); + } + catch (exception& e) { + cout << "Exception: " << e.what() << crt; + } + + + /* + PronadjiPosiljku vraca pokazivac na posiljku sa proslijedjenom oznakom. + Ako posiljka nije pronadjena, metoda vraca nullptr. + */ + + string oznakaKnjige = knjige.GetOznaka(); + + Posiljka* pronadjena = + brzaPosta.PronadjiPosiljku(oznakaKnjige); + + if (pronadjena != nullptr) + cout << "Pronadjena posiljka: " + << pronadjena->GetOznaka() << crt; + + if (brzaPosta.PronadjiPosiljku("BH-ST-9999-XX") == nullptr) + cout << "Nepostojeca posiljka nije pronadjena" << crt; + + + /* + EvidentirajStatus pronalazi posiljku na osnovu oznake i pokusava + dodati + novi status koristeci pravila metode DodajStatus. + Nakon svakog uspjesno evidentiranog statusa potrebno je u zasebnom + threadu + poslati obavijest primaocu. Obavijest se ne salje ako posiljka ne + postoji ili status nije dodan, te u tim slucajevima metoda vraca + false. + Primjer sadrzaja obavijesti: + ------------------------------------------------------------------ + --------------- + To: goran@fit.ba + From: info@brzaposta.ba + Subject: Promjena statusa posiljke + Postovani Goran Skondric, + Posiljka BH-ST-0007-GS je promijenila status u PREUZETA. + Vrijeme evidentiranja: 01.07.2026 09:00 + Hvala sto koristite nase usluge. + BrzaPosta Tim + ------------------------------------------------------------------ + --------------- + */ + + if (brzaPosta.EvidentirajStatus( + oznakaKnjige, + PREUZETA, + preuzeta)) + cout << "Status evidentiran i obavijest poslana" << crt; + + if (!brzaPosta.EvidentirajStatus( + oznakaKnjige, + ISPORUCENA, + isporucena)) + cout << "Obavijest nije poslana za neispravnu promjenu " + << "statusa" << crt; + + if (!brzaPosta.EvidentirajStatus( + "NEPOSTOJECA", + PREUZETA, + preuzeta)) + cout << "Obavijest nije poslana za nepostojecu posiljku" + << crt; + + + /* + PosiljkePoStatusu vraca vector pokazivaca na sve posiljke ciji trenutni + status odgovara proslijedjenom statusu. + + */ + vector preuzetePosiljke = + brzaPosta.PosiljkePoStatusu(PREUZETA); + + for (auto posiljka : preuzetePosiljke) + cout << posiljka->GetOznaka() << " -> " + << StatusPosiljkeNazivi[ + (int)posiljka->GetTrenutniStatus() + ] + << crt; + + + /* + StatistikaPoStatusu vraca kolekciju koja za svaki status, + redoslijedom + definisanim enumom StatusPosiljke, sadrzi status i broj posiljki koje se + trenutno nalaze u tom statusu. U rezultat se dodaje svih pet statusa, + ukljucujuci i one za koje je broj posiljki jednak nuli. + */ + + Kolekcija statistika = + brzaPosta.StatistikaPoStatusu(); + + for (int i = 0; i < statistika.GetTrenutno(); i++) + cout << StatusPosiljkeNazivi[ + (int)statistika.GetElement1(i) + ] + << " -> " + << statistika.GetElement2(i) + << crt; + + + DostavnaSluzba kopijaSluzbe = brzaPosta; + + cout << kopijaSluzbe.GetNaziv() << " ima " + << kopijaSluzbe.GetPosiljke().size() + << " posiljki" << crt; + + cin.get(); + + return 0; +} From 9b2ece103f7d1ea7023fe792957233276051e35d Mon Sep 17 00:00:00 2001 From: null68 Date: Wed, 19 Aug 2026 19:06:04 +0200 Subject: [PATCH 4/6] feat: readme --- Ispiti/2026-07-09/README.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 Ispiti/2026-07-09/README.md diff --git a/Ispiti/2026-07-09/README.md b/Ispiti/2026-07-09/README.md new file mode 100644 index 0000000..866a46b --- /dev/null +++ b/Ispiti/2026-07-09/README.md @@ -0,0 +1,6 @@ +# Grupa 1 + +- enum VrstaDostave { STANDARDNA, BRZA, MEDJUNARODNA }; +- enum StatusPosiljke { KREIRANA, PREUZETA, U_TRANZITU, ISPORUCENA, OTKAZANA }; +- Kolekcija statički niz pointera (T* elementi[max]) +- string, vector, advanced regex, generisi oznaku From e4fc5037bb4b91b411900d6e49baecbf557dc600 Mon Sep 17 00:00:00 2001 From: null68 Date: Fri, 21 Aug 2026 20:49:51 +0200 Subject: [PATCH 5/6] feat: possible exam solution g2 --- .../2026-07-09/PR2_2026_07_09_G2_Postavka.cpp | 547 +++++++++++ .../PR2_2026_07_09_G2_Rje\305\241enje.cpp" | 853 ++++++++++++++++++ Ispiti/2026-07-09/README.md | 8 +- 3 files changed, 1407 insertions(+), 1 deletion(-) create mode 100644 Ispiti/2026-07-09/PR2_2026_07_09_G2_Postavka.cpp create mode 100644 "Ispiti/2026-07-09/PR2_2026_07_09_G2_Rje\305\241enje.cpp" diff --git a/Ispiti/2026-07-09/PR2_2026_07_09_G2_Postavka.cpp b/Ispiti/2026-07-09/PR2_2026_07_09_G2_Postavka.cpp new file mode 100644 index 0000000..6b8e61d --- /dev/null +++ b/Ispiti/2026-07-09/PR2_2026_07_09_G2_Postavka.cpp @@ -0,0 +1,547 @@ +#include +using namespace std; + +string crt = "\n-------------------------------------------------------------------------------- - \n"; + +string PORUKA_TELEFON = crt + + "TELEFONE ISKLJUCITE I ODLOZITE U TORBU, DZEP ILI DRUGU LOKACIJU VAN DOHVATA.\n" + "CESTO SE NA TELEFONIMA (PRO)NALAZE PROGRAMSKI KODOVI KOJI MOGU BITI ISKORISTENI ZA\n" + "RJESAVANJE ISPITNOG ZADATKA, STO CE, U SLUCAJU PRONALASKA, BITI SANKCIONISANO.\n\n" + "SVI KOD KOJIH BUDE PRONADJEN TELEFON ILI NEKI DRUGI UREDJAJ KOJI MOZE SADRZAVATI\n" + "MATERIJAL ILI RJESENJA ISPITNIH ZADATAKA CE BITI UDALJENI SA ISPITA I BIT CE POKRENUT\n" + "POSTUPAK PROTIV NJIH" + crt; + +string PORUKA_ISPIT = crt + + "0. PROVJERITE DA LI ZADACI PRIPADAJU VASOJ GRUPI (G1/G2)\n" + "1. SVE KLASE SA DINAMICKOM ALOKACIJOM MORAJU IMATI ISPRAVAN DESTRUKTOR\n" + "2. IZOSTAVLJANJE DESTRUKTORA ILI NJEGOVIH DIJELOVA BIT CE OZNACENO KAO TM\n" + "3. ATRIBUTI, METODE I PARAMETRI MORAJU BITI IDENTICNI ONIMA U TESTNOJ MAIN FUNKCIJI, \n" + " OSIM AKO POSTOJI JASNO OPISAN RAZLOG ZA MODIFIKACIJU\n" + "4. IZUZETKE BACAJTE SAMO TAMO GDJE JE IZRICITO NAGLASENO\n" + "5. SVE METODE KOJE SE POZIVAJU U MAIN-U MORAJU POSTOJATI.\n" + " AKO NEMATE ZELJENU IMPLEMENTACIJU, OSTAVITE PRAZNO TIJELO ILI VRATITE DEFAULT VRIJEDNOST\n" + "6. U MAIN FUNKCIJI MOZETE DODAVATI TESTNE PODATKE I POZIVE PO VLASTITOM IZBORU\n" + "7. TESTIRAJTE PROGRAM U OBA MODA (F5 i Ctrl+F5)" + crt; + +char* AlocirajTekst(const char* tekst) { + if (tekst == nullptr) return nullptr; + size_t velicina = strlen(tekst) + 1; + char* noviTekst = new char[velicina]; + strcpy_s(noviTekst, velicina, tekst); + return noviTekst; +} + +enum StatusZahtjeva { ZAPRIMLJEN, DIJAGNOSTIKA, POPRAVKA, ZAVRSEN }; + +const char* StatusZahtjevaNazivi[] = { + "ZAPRIMLJEN", "DIJAGNOSTIKA", "POPRAVKA", "ZAVRSEN" +}; + +template +class Kolekcija { + T1* _elementi1; + T2* _elementi2; + int _trenutno; + +public: + Kolekcija() : _trenutno(0) { + _elementi1 = new T1[max]; + _elementi2 = new T2[max]; + } + + T1& GetElement1(int indeks) { + return _elementi1[indeks]; + } + + const T1& GetElement1(int indeks) const { + return _elementi1[indeks]; + } + + T2& GetElement2(int indeks) { + return _elementi2[indeks]; + } + + const T2& GetElement2(int indeks) const { + return _elementi2[indeks]; + } + + int GetTrenutno() const { return _trenutno; } + + friend ostream& operator<<(ostream& COUT, const Kolekcija& obj) { + for (int i = 0; i < obj.GetTrenutno(); i++) + COUT << obj.GetElement1(i) << " " << obj.GetElement2(i) << endl; + return COUT; + } + + ~Kolekcija() { + delete[] _elementi1; + delete[] _elementi2; + _elementi1 = nullptr; + _elementi2 = nullptr; + _trenutno = 0; + } +}; + +class DatumVrijeme { + int* _dan, *_mjesec, *_godina, *_sati, *_minute; + +public: + DatumVrijeme(int dan = 1, int mjesec = 1, int godina = 2000, + int sati = 0, int minute = 0) { + _dan = new int(dan); + _mjesec = new int(mjesec); + _godina = new int(godina); + _sati = new int(sati); + _minute = new int(minute); + } + + int GetGodina() const { return *_godina; } + + friend ostream& operator<<(ostream& COUT, const DatumVrijeme& obj) { + // ToString vraca datum i vrijeme u formatu DD.MM.GGGG HH:MM + COUT << obj.ToString(); + return COUT; + } + + ~DatumVrijeme() { + delete _dan; + delete _mjesec; + delete _godina; + delete _sati; + delete _minute; + _dan = _mjesec = _godina = _sati = _minute = nullptr; + } +}; + +class Intervencija { + string _opis; + string _tehnicar; + double _cijena; + int _trajanjeMinuta; + +public: + const string& GetOpis() const { return _opis; } + const string& GetTehnicar() const { return _tehnicar; } + double GetCijena() const { return _cijena; } + int GetTrajanjeMinuta() const { return _trajanjeMinuta; } + + friend ostream& operator<<(ostream& COUT, const Intervencija& obj) { + COUT << obj.ToString(); + return COUT; + } + + /* + ToString vraca podatke u formatu : + opis | tehnicar | cijena KM | trajanje min + Zamjena diska | Haris Hadzic | 85.50 KM | 45 min + */ +}; + +class Klijent { + string _imePrezime; + string _email; + string _telefon; + +public: + const string& GetImePrezime() const { return _imePrezime; } + const string& GetEmail() const { return _email; } + const string& GetTelefon() const { return _telefon; } + + friend ostream& operator<<(ostream& COUT, const Klijent& obj) { + COUT << obj.GetImePrezime() << " | " << obj.GetEmail() + << " | " << obj.GetTelefon(); + return COUT; + } +}; + +class ServisniZahtjev { + char* _oznaka; + char* _uredjaj; + char* _opisKvara; + int _redniBroj; + Klijent _klijent; + Kolekcija _statusi; + vector _intervencije; + +public: + const char* GetOznaka() const { return _oznaka; } + const char* GetUredjaj() const { return _uredjaj; } + const char* GetOpisKvara() const { return _opisKvara; } + int GetRedniBroj() const { return _redniBroj; } + + Klijent& GetKlijent() { return _klijent; } + const Klijent& GetKlijent() const { return _klijent; } + + Kolekcija& GetStatusi() { + return _statusi; + } + + const Kolekcija& GetStatusi() const { + return _statusi; + } + + vector& GetIntervencije() { return _intervencije; } + + const vector& GetIntervencije() const { + return _intervencije; + } + + StatusZahtjeva GetTrenutniStatus() const { + return _statusi.GetElement1(_statusi.GetTrenutno() - 1); + } + + friend ostream& operator<<(ostream& COUT, const ServisniZahtjev& obj) { + // ToString vraca: + // oznaka | ime i prezime klijenta | uredjaj | trenutni status + COUT << obj.ToString(); + return COUT; + } + + ~ServisniZahtjev() { + delete[] _oznaka; + delete[] _uredjaj; + delete[] _opisKvara; + _oznaka = _uredjaj = _opisKvara = nullptr; + } +}; + +class Servis { + char* _naziv; + vector _zahtjevi; + +public: + Servis(const char* naziv = "") { + _naziv = AlocirajTekst(naziv); + } + + const char* GetNaziv() const { return _naziv; } + + vector& GetZahtjevi() { return _zahtjevi; } + + const vector& GetZahtjevi() const { + return _zahtjevi; + } + + ~Servis() { + delete[] _naziv; + _naziv = nullptr; + } +}; + +const char* GetOdgovorNaPrvoPitanje() { + cout << "Pitanje -> Pojasnite zbog cega se operator<< implementira " + "kao globalna a ne funkcija clanica, te na koji nacin se implementiraju " + "prefiksalni i postfiksni oblik operatora inkrementiranja?\n"; + return "Odgovor -> OVDJE UNESITE VAS ODGOVOR"; +} + +const char* GetOdgovorNaDrugoPitanje() { + cout << "Pitanje -> Pojasnite nacin na koji biste, koristeci " + "obradjene klase i metode, mogli saznati velicinu nekog tekstualnog fajla.\n"; + return "Odgovor -> OVDJE UNESITE VAS ODGOVOR"; +} + +int main() { + cout << PORUKA_TELEFON; + cin.get(); + + cout << PORUKA_TELEFON; + cin.get(); + + cout << PORUKA_ISPIT; + cin.get(); + + system("cls"); + + cout << GetOdgovorNaPrvoPitanje() << crt; + cin.get(); + + cout << GetOdgovorNaDrugoPitanje() << crt; + cin.get(); + + /* + Funkcija GenerisiOznaku generise oznaku servisnog zahtjeva u formatu: + SRV-BBB/IN-GGGG + + Potpis funkcije treba biti: + string GenerisiOznaku(const char* imePrezime, int redniBroj, int godina) + + SRV -> fiksni prefiks + BBB -> redni broj zahtjeva popunjen nulama na slobodnim mjestima + IN -> inicijali imena i prezimena klijenta + GGGG -> godina zaprimanja zahtjeva. + + Kod imena koje sadrzi vise rijeci koriste se inicijal prve i posljednje + rijeci. + + Redni broj mora biti u rasponu 1-999, a godina 2000-2099. + Za neispravne podatke funkcija vraca "SRV-000/XX-0000". + */ + + if (GenerisiOznaku("Amina Buric", 42, 2026) == "SRV-042/AB-2026") + cout << "Oznaka OK" << crt; + + if (GenerisiOznaku("Goran Skondric", 7, 2026) == "SRV-007/GS 2026") + cout << "Oznaka OK" << crt; + + if (GenerisiOznaku("Ana Marija Kovac", 156, 2027) == + "SRV-156/AK-2027") + cout << "Oznaka OK" << crt; + + if (GenerisiOznaku("Amina", 42, 2026) == "SRV-000/XX-0000" && + GenerisiOznaku("Amina Buric", 0, 2026) == "SRV-000/XX-0000" && + GenerisiOznaku("Amina Buric", 1000, 2026) == "SRV-000/XX-0000" && + GenerisiOznaku("Amina Buric", 42, 1999) == "SRV-000/XX-0000") + cout << "Neispravni podaci za oznaku OK" << crt; + + /* + Koristeci regex, funkcija ValidirajOznaku provjerava prethodno + definisani format. Prefiks mora biti SRV, redni broj mora imati + tri cifre i ne moze biti 000, inicijali moraju biti velika slova, + a godina je 2000-2099. + + Potpis funkcije treba biti: + bool ValidirajOznaku(const string& oznaka) + */ + + if (ValidirajOznaku("SRV-042/AB-2026")) + cout << "OZNAKA VALIDNA" << crt; + + if (!ValidirajOznaku("SRV/042-AB-2026") && + !ValidirajOznaku("SRV-42/AB-2026") && + !ValidirajOznaku("SRV-042/Ab-2026") && + !ValidirajOznaku("SRV-000/AB-2026") && + !ValidirajOznaku("2026-SRV-042/AB")) + cout << "OZNAKA NIJE VALIDNA" << crt; + + Kolekcija brojevi; + brojevi.Dodaj(10, "Deset"); + brojevi.Dodaj(20, "Dvadeset"); + brojevi.Dodaj(10, "Deset"); + brojevi.Dodaj(30, "Trideset"); + brojevi.Dodaj(20, "Dvadeset"); + + cout << brojevi << crt; + + /* + UkloniDuplikate vraca novu kolekciju u kojoj je zadrzano samo prvo + pojavljivanje svakog para. Par predstavlja duplikat iskljucivo ako su + jednaki i prvi i drugi element. Originalna kolekcija ostaje nepromijenjena. + */ + + Kolekcija bezDuplikata = brojevi.UkloniDuplikate(); + + cout << "Bez duplikata:" << crt << bezDuplikata; + cout << "Original:" << crt << brojevi; + + try { + Kolekcija puna; + puna.Dodaj(1, "Jedan"); + puna.Dodaj(2, "Dva"); + puna.Dodaj(3, "Tri"); + } + catch (exception& e) { + cout << "Exception: " << e.what() << crt; + } + + Kolekcija kopijaBrojeva = brojevi; + kopijaBrojeva[0] = 100; + + Kolekcija dodijeljeniBrojevi; + dodijeljeniBrojevi = brojevi; + dodijeljeniBrojevi.GetElement2(0) = "Izmijenjeno"; + + cout << "Original:" << crt << brojevi; + cout << "Kopija:" << crt << kopijaBrojeva; + cout << "Dodijeljeni objekat:" << crt << dodijeljeniBrojevi; + + DatumVrijeme zaprimljen(9, 7, 2026, 8, 0); + DatumVrijeme dijagnostika(9, 7, 2026, 9, 0); + DatumVrijeme popravka(9, 7, 2026, 10, 0); + DatumVrijeme zavrsen(9, 7, 2026, 11, 0); + + /* + ToString vraca datum i vrijeme u formatu DD.MM.GGGG HH:MM, + ukljucujuci pocetne nule. + */ + + cout << zaprimljen.ToString() << crt; // 09.07.2026 08:00 + + if (dijagnostika > zaprimljen) + cout << "Vrijeme dijagnostike je nakon zaprimanja" << crt; + + DatumVrijeme kopijaDatuma(dijagnostika); + + if (kopijaDatuma == dijagnostika && !(zaprimljen == dijagnostika)) + cout << "Provjera vremena, OK." << crt; + + Klijent amina("Amina Buric", "amina@fit.ba", "061-111-222"); + Klijent goran("Goran Skondric", "goran@fit.ba", "062-222-333"); + Klijent kopijaKlijenta = amina; + + cout << kopijaKlijenta << crt; + + /* + Intervencija sadrzi opis, ime tehnicara, cijenu i trajanje u minutama. + */ + + Intervencija pregled("Dijagnostika uredjaja", "Haris Hadzic", 20, 30); + Intervencija zamjenaDiska("Zamjena diska", "Haris Hadzic", 85.5, 45); + Intervencija instalacija("Instalacija sistema", "Maja Majic", 35, 60); + + /* + ToString vraca podatke u formatu: + opis | tehnicar | cijena KM | trajanje min + Zamjena diska | Haris Hadzic | 85.50 KM | 45 min + */ + + cout << zamjenaDiska.ToString() << crt; + + /* + Konstruktor ServisnogZahtjeva generise oznaku na osnovu klijenta, + rednog broja i godine zaprimanja, te evidentira pocetni status + ZAPRIMLJEN sa proslijedjenim vremenom. + */ + + ServisniZahtjev laptop("Laptop", "Ne pokrece se", amina, 42, zaprimljen); + ServisniZahtjev telefon("Telefon", "Razbijen ekran", goran, 7, zaprimljen); + + /* + ToString vraca podatke u formatu: + oznaka | ime i prezime klijenta | uredjaj | trenutni status + + SRV-042/AB-2026 | Amina Buric | Laptop | ZAPRIMLJEN + */ + + cout << laptop.ToString() << crt; + + if (laptop.ToString() == + "SRV-042/AB-2026 | Amina Buric | Laptop | ZAPRIMLJEN") + cout << "ServisniZahtjev ToString OK" << crt; + + /* + DodajStatus dodaje status samo ako je vrijeme vece od vremena posljednjeg + statusa i ako status predstavlja neposredno sljedecu fazu. + + Dozvoljen slijed je: + ZAPRIMLJEN -> DIJAGNOSTIKA -> POPRAVKA -> ZAVRSEN + + Preskakanje ili ponavljanje statusa nije dozvoljeno. Nakon statusa ZAVRSEN + nisu dozvoljene nove promjene. Neuspjesan pokusaj vraca false bez izmjene + kolekcije statusa. + */ + + if (!laptop.DodajStatus(POPRAVKA, dijagnostika)) + cout << "Preskakanje statusa nije dozvoljeno" << crt; + + if (laptop.DodajStatus(DIJAGNOSTIKA, dijagnostika)) + cout << "Status DIJAGNOSTIKA dodan" << crt; + + if (!laptop.DodajStatus(DIJAGNOSTIKA, popravka)) + cout << "Ponavljanje statusa nije dozvoljeno" << crt; + + /* + DodajIntervenciju dodaje intervenciju samo dok je zahtjev u statusu + DIJAGNOSTIKA ili POPRAVKA, uz cijenu i trajanje vece od nule. + + Metoda vraca true ako je intervencija dodana, u suprotnom false. + UkupnaCijena vraca zbir cijena svih intervencija, a UkupnoTrajanje + zbir njihovih trajanja u minutama. + */ + + if (laptop.DodajIntervenciju(pregled)) + cout << "Intervencija dodana" << crt; + + if (laptop.DodajStatus(POPRAVKA, popravka)) + cout << "Status POPRAVKA dodan" << crt; + + laptop.DodajIntervenciju(zamjenaDiska); + laptop.DodajIntervenciju(instalacija); + + cout << "Ukupna cijena: " << laptop.UkupnaCijena() << " KM" << crt; + cout << "Ukupno trajanje: " << laptop.UkupnoTrajanje() << " min" << crt; + + ServisniZahtjev kopijaLaptopa = laptop; + cout << kopijaLaptopa << crt; + + Servis fitServis("FIT Servis"); + + /* + DodajZahtjev dodaje zahtjev u servis. Nije dozvoljeno dodati dva zahtjeva + sa istim rednim brojem ili istom oznakom. U slucaju duplikata metoda baca + izuzetak. + */ + + fitServis.DodajZahtjev(laptop); + fitServis.DodajZahtjev(telefon); + + try { + fitServis.DodajZahtjev(telefon); + } + catch (exception& e) { + cout << "Exception: " << e.what() << crt; + } + + /* + PronadjiZahtjev vraca pokazivac na zahtjev sa proslijedjenom oznakom. + Ako zahtjev nije pronadjen, metoda vraca nullptr. + */ + + string oznakaLaptopa = laptop.GetOznaka(); + ServisniZahtjev* pronadjen = fitServis.PronadjiZahtjev(oznakaLaptopa); + + if (pronadjen != nullptr) + cout << "Pronadjen zahtjev: " << pronadjen->GetOznaka() << crt; + + if (fitServis.PronadjiZahtjev("SRV-999/XX-2026") == nullptr) + cout << "Zahtjev nije pronadjen" << crt; + + /* + EvidentirajStatus pronalazi zahtjev i pokusava dodati status koristeci + pravila metode DodajStatus. + + Racun se salje u zasebnom threadu iskljucivo nakon uspjesnog evidentiranja + statusa ZAVRSEN. Za ostale statuse se ne salju obavijesti. + + Ako zahtjev ne postoji ili status nije dodan, metoda vraca false. + */ + + if (fitServis.EvidentirajStatus(oznakaLaptopa, ZAVRSEN, zavrsen)) + cout << "Zahtjev zavrsen i racun poslan" << crt; + + if (!fitServis.EvidentirajStatus( + oznakaLaptopa, ZAVRSEN, DatumVrijeme(9, 7, 2026, 12, 0))) + cout << "Ponavljanje zavrsnog statusa nije dozvoljeno" << crt; + + if (!fitServis.EvidentirajStatus( + "NEPOSTOJECI", DIJAGNOSTIKA, dijagnostika)) + cout << "Status nije evidentiran za nepostojeci zahtjev" << crt; + + /* + IzdvojiNezavrsene vraca vector pokazivaca na sve zahtjeve ciji trenutni + status nije ZAVRSEN. + */ + + vector nezavrseni = fitServis.IzdvojiNezavrsene(); + + for (auto zahtjev : nezavrseni) + cout << zahtjev->GetOznaka() << " -> " + << StatusZahtjevaNazivi[(int)zahtjev->GetTrenutniStatus()] << crt; + + /* + IzracunajPrihod vraca zbir cijena svih intervencija evidentiranih + zahtjevima koji imaju status ZAVRSEN. Intervencije na nezavrsenim + zahtjevima ne ulaze u prihod. + */ + + cout << "Ostvareni prihod: " << fitServis.IzracunajPrihod() + << " KM" << crt; + + Servis kopijaServisa = fitServis; + + cout << kopijaServisa.GetNaziv() << " ima " + << kopijaServisa.GetZahtjevi().size() << " zahtjeva" << crt; + + cin.get(); + return 0; +} diff --git "a/Ispiti/2026-07-09/PR2_2026_07_09_G2_Rje\305\241enje.cpp" "b/Ispiti/2026-07-09/PR2_2026_07_09_G2_Rje\305\241enje.cpp" new file mode 100644 index 0000000..2b6f3d5 --- /dev/null +++ "b/Ispiti/2026-07-09/PR2_2026_07_09_G2_Rje\305\241enje.cpp" @@ -0,0 +1,853 @@ +#include +#include +#include +#include +#include +#include + + +using namespace std; + +string crt = "\n-------------------------------------------------------------------------------- - \n"; +string PORUKA_TELEFON = crt + "TELEFONE ISKLJUCITE I ODLOZITE U TORBU, DZEP ILI DRUGU LOKACIJU VAN DOHVATA.\n" +"CESTO SE NA TELEFONIMA (PRO)NALAZE PROGRAMSKI KODOVI KOJI MOGU BITI ISKORISTENI ZA\n" +"RJESAVANJE ISPITNOG ZADATKA, STO CE, U SLUCAJU PRONALASKA, BITI SANKCIONISANO.\n\n" +"SVI KOD KOJIH BUDE PRONADJEN TELEFON ILI NEKI DRUGI UREDJAJ KOJI MOZE SADRZAVATI\n" +"MATERIJAL ILI RJESENJA ISPITNIH ZADATAKA CE BITI UDALJENI SA ISPITA I BIT CE POKRENUT\n" +"POSTUPAK PROTIV NJIH" + crt; + +string PORUKA_ISPIT = crt + +"0. PROVJERITE DA LI ZADACI PRIPADAJU VASOJ GRUPI (G1/G2)\n" +"1. SVE KLASE SA DINAMICKOM ALOKACIJOM MORAJU IMATI ISPRAVAN DESTRUKTOR\n" +"2. IZOSTAVLJANJE DESTRUKTORA ILI NJEGOVIH DIJELOVA BIT CE OZNACENO KAO TM\n" +"3. ATRIBUTI, METODE I PARAMETRI MORAJU BITI IDENTICNI ONIMA U TESTNOJ MAIN FUNKCIJI, \n" +" OSIM AKO POSTOJI JASNO OPISAN RAZLOG ZA MODIFIKACIJU\n" +"4. IZUZETKE BACAJTE SAMO TAMO GDJE JE IZRICITO NAGLASENO\n" +"5. SVE METODE KOJE SE POZIVAJU U MAIN-U MORAJU POSTOJATI.\n" +" AKO NEMATE ZELJENU IMPLEMENTACIJU, OSTAVITE PRAZNO TIJELO ILI VRATITE DEFAULT VRIJEDNOST\n" +"6. U MAIN FUNKCIJI MOZETE DODAVATI TESTNE PODATKE I POZIVE PO VLASTITOM IZBORU\n" +"7. TESTIRAJTE PROGRAM U OBA MODA (F5 i Ctrl+F5)" + crt; + + +char* AlocirajTekst(const char* tekst) { + if (tekst == nullptr) return nullptr; + size_t velicina = strlen(tekst) + 1; + char* noviTekst = new char[velicina]; + strcpy_s(noviTekst, velicina, tekst); + return noviTekst; +} + +enum StatusZahtjeva { ZAPRIMLJEN, DIJAGNOSTIKA, POPRAVKA, ZAVRSEN }; + +const char* StatusZahtjevaNazivi[] = { + "ZAPRIMLJEN", "DIJAGNOSTIKA", "POPRAVKA", "ZAVRSEN" +}; + + +string GenerisiOznaku(const char* imePrezime, int redniBroj, int godina) { + if (redniBroj < 1 || redniBroj > 999) { + return "SRV-000/XX-0000"; + } + + if (godina < 2000 || godina > 2099) { + return "SRV-000/XX-0000"; + } + + ostringstream buffer; + + string s_imePrezime = imePrezime; + + size_t lastSpace = s_imePrezime.find_last_of(' '); + + if (lastSpace == string::npos || lastSpace == s_imePrezime.size() - 1) { + return "SRV-000/XX-0000"; + } + + buffer << "SRV-" << setfill('0') << setw(3) << redniBroj << "/" << imePrezime[0] << imePrezime[lastSpace + 1] << "-" << godina; + + return buffer.str(); +} + +/* +Koristeci regex, funkcija ValidirajOznaku provjerava prethodno +definisani + format. Prefiks mora biti SRV, redni broj mora imati tri cifre i +ne moze + biti 000, inicijali moraju biti velika slova, a godina je 2000- +2099. + Potpis funkcije treba biti: + bool ValidirajOznaku(const string& oznaka) + */ +bool ValidirajOznaku(const string& oznaka) { + return regex_match(oznaka, regex("SRV-(?!000)[0-9]{3}/[A-Z]{2}-20([0-9]{2})")); +} + +template +class Kolekcija { + T1* _elementi1; + T2* _elementi2; + int _trenutno; +public: + Kolekcija() : _trenutno(0) { + _elementi1 = new T1[max]; + _elementi2 = new T2[max]; + } + + Kolekcija(const Kolekcija& other) : _trenutno(other._trenutno) { + _elementi1 = new T1[max]; + _elementi2 = new T2[max]; + + for (int i = 0; i < _trenutno; i++) { + _elementi1[i] = other._elementi1[i]; + _elementi2[i] = other._elementi2[i]; + } + } + + Kolekcija& operator=(const Kolekcija& other) { + if (this == &other) return *this; + _trenutno = other._trenutno; + + delete[] _elementi1; + delete[] _elementi2; + _elementi1 = nullptr; + _elementi2 = nullptr; + + _elementi1 = new T1[max]; + _elementi2 = new T2[max]; + + for (int i = 0; i < _trenutno; i++) { + _elementi1[i] = other._elementi1[i]; + _elementi2[i] = other._elementi2[i]; + } + return *this; + } + + T1& operator [] (int idx) { + return _elementi1[idx]; + } + + int GetTrenutno() const { return _trenutno; } + + const T1& GetElement1(int indeks) const { + return _elementi1[indeks]; + } + + const T2& GetElement2(int indeks) const { + return _elementi2[indeks]; + } + + T2& GetElement2(int index) { + return _elementi2[index]; + } + + friend ostream& operator<<(ostream& COUT, const Kolekcija& obj) { + for (int i = 0; i < obj.GetTrenutno(); i++) + COUT << obj.GetElement1(i) << " " << obj.GetElement2(i) << + endl; + return COUT; + } + ~Kolekcija() { + delete[] _elementi1; + delete[] _elementi2; + _elementi1 = nullptr; + _elementi2 = nullptr; + _trenutno = 0; + } + + void Dodaj(T1 el1, T2 el2) { + if (_trenutno >= max) { + throw exception("collection full"); + }; + _elementi1[_trenutno] = el1; + _elementi2[_trenutno] = el2; + + _trenutno++; + } + + Kolekcija UkloniDuplikate() { + Kolekcija nova; + + for (int i = 0; i < _trenutno; i++) { + int postoji = false; + for (int j = 0; i < _trenutno; j++) { + if (_elementi1[i] == nova.GetElement1(j) && _elementi2[i] == nova.GetElement2(j)) { + postoji = true; + break; + } + } + + if (!postoji) { + nova.Dodaj(_elementi1[i], _elementi2[i]); + } + } + + return nova; + } + +}; + + + + + +class DatumVrijeme { + int* _dan, * _mjesec, * _godina, * _sati, * _minute; +public: + DatumVrijeme(int dan = 1, int mjesec = 1, int godina = 2000, + int sati = 0, int minute = 0) { + _dan = new int(dan); + _mjesec = new int(mjesec); + _godina = new int(godina); + _sati = new int(sati); + _minute = new int(minute); + } + + DatumVrijeme(const DatumVrijeme& other) { + _dan = new int(*other._dan); + _mjesec = new int(*other._mjesec); + _godina = new int(*other._godina); + _sati = new int(*other._sati); + _minute = new int(*other._minute); + } + DatumVrijeme& operator=(const DatumVrijeme& other) { + if (this == &other) return *this; + delete _dan; + delete _mjesec; + delete _godina; + delete _sati; + delete _minute; + _dan = _mjesec = _godina = _sati = _minute = nullptr; + + _dan = new int(*other._dan); + _mjesec = new int(*other._mjesec); + _godina = new int(*other._godina); + _sati = new int(*other._sati); + _minute = new int(*other._minute); + return *this; + } + int GetGodina() const { return *_godina; } + friend ostream& operator<<(ostream& COUT, const DatumVrijeme& obj) + { + // ToString vraca datum i vrijeme u formatu DD.MM.GGGG HH:MM + COUT << obj.ToString(); + return COUT; + } + + string ToString() const { + ostringstream buffer; + + buffer << setfill('0') << setw(2) << *_dan << "."; + buffer << setfill('0') << setw(2) << *_mjesec << "."; + buffer << setfill('0') << setw(4) << *_godina << " "; + buffer << setfill('0') << setw(2) << *_sati << ":"; + buffer << setfill('0') << setw(2) << *_minute << endl; + + + return buffer.str(); + } + + ~DatumVrijeme() { + delete _dan; + delete _mjesec; + delete _godina; + delete _sati; + delete _minute; + _dan = _mjesec = _godina = _sati = _minute = nullptr; + } + + bool operator > (const DatumVrijeme& other) const { + if (*_godina != *other._godina) return *_godina > *other._godina; + if (*_mjesec != *other._mjesec) return *_mjesec > *other._mjesec; + if (*_dan != *other._dan) return *_dan > *other._dan; + + if (*_sati != *other._sati) return *_sati > *other._sati; + + + return *_minute > *other._minute; + } + + bool operator == (const DatumVrijeme& other) const { + return *_godina == *other._godina && + *_mjesec == *other._mjesec && + *_dan == *other._dan && + *_sati == *other._sati && + *_minute == *other._minute; + } + +}; + +class Intervencija { + string _opis; + string _tehnicar; + double _cijena; + int _trajanjeMinuta; +public: + const string& GetOpis() const { return _opis; } + const string& GetTehnicar() const { return _tehnicar; } + double GetCijena() const { return _cijena; } + int GetTrajanjeMinuta() const { return _trajanjeMinuta; } + friend ostream& operator<<(ostream& COUT, const Intervencija& obj) + { + /* + ToString vraca podatke u formatu : + opis | tehnicar | cijena KM | trajanje min + Zamjena diska | Haris Hadzic | 85.50 KM | 45 min + */ + COUT << obj.ToString(); + return COUT; + } + Intervencija(const char* opis, const char* tehnicar, double cijena, int trajanje) : _opis(opis), _tehnicar(tehnicar), _cijena(cijena), _trajanjeMinuta(trajanje) {} + + string ToString() const { + ostringstream buffer; + + buffer << GetOpis() << " | " << GetTehnicar() << " | " << GetCijena() << " | " << GetTrajanjeMinuta() << endl; + + return buffer.str(); + } + +}; + + + +class Klijent { + string _imePrezime; + string _email; + string _telefon; +public: + Klijent(const char* imePrezime, const char* email, const char* telefon) { + _imePrezime = imePrezime; + _email = email; + _telefon = telefon; + } + // ovdje inace nisu potrebni copy constructori, oni se inace prave kada imas pointer u klasi, npr da je bilo char* _imePrezime, ovako ga obicno ne moras pravit jer nema brige hocel se desit shallow copy + Klijent(const Klijent& other) { + _imePrezime = other.GetImePrezime(); + _email = other.GetEmail(); + _telefon = other.GetTelefon(); + } + + Klijent& operator=(const Klijent& other) { + if (this == &other) return *this; + _imePrezime = other.GetImePrezime(); + _email = other.GetEmail(); + _telefon = other.GetTelefon(); + return *this; + } + const string& GetImePrezime() const { return _imePrezime; } + const string& GetEmail() const { return _email; } + const string& GetTelefon() const { return _telefon; } + friend ostream& operator<<(ostream& COUT, const Klijent& obj) { + COUT << obj.GetImePrezime() << " | " << obj.GetEmail() + << " | " << obj.GetTelefon(); + return COUT; + } +}; + + + +class ServisniZahtjev { + char* _oznaka; + char* _uredjaj; + char* _opisKvara; + int _redniBroj; + Klijent _klijent; + Kolekcija _statusi; + vector _intervencije; +public: + const char* GetOznaka() const { return _oznaka; } + const char* GetUredjaj() const { return _uredjaj; } + const char* GetOpisKvara() const { return _opisKvara; } + int GetRedniBroj() const { return _redniBroj; } + Klijent& GetKlijent() { return _klijent; } + const Klijent& GetKlijent() const { return _klijent; } + Kolekcija& GetStatusi() { + return _statusi; + } + const Kolekcija& GetStatusi() + const { + return _statusi; + } + vector& GetIntervencije() { return _intervencije; } + const vector& GetIntervencije() const { + return + _intervencije; + } + StatusZahtjeva GetTrenutniStatus() const { + return + _statusi.GetElement1(_statusi.GetTrenutno() - 1); + } + friend ostream& operator<<(ostream& COUT, const ServisniZahtjev& obj) { + // ToString vraca: + // oznaka | ime i prezime klijenta | uredjaj | trenutni status + COUT << obj.ToString(); + return COUT; + } + + string ToString() const { + ostringstream buffer; + + buffer << GetOznaka() << " | " << GetKlijent().GetImePrezime() << " | " << GetUredjaj() << " | " << StatusZahtjevaNazivi[GetTrenutniStatus()] << endl; + + return buffer.str(); + } + ~ServisniZahtjev() { + delete[] _oznaka; + delete[] _uredjaj; + delete[] _opisKvara; + _oznaka = _uredjaj = _opisKvara = nullptr; + } + // : _klijent(klijent) se mora dodat ako ne zelis da se peglas sa jednom greskom vec 20 minuta :D + ServisniZahtjev(const char* uredjaj, const char* opisKvara, const Klijent& klijent, int redniBroj, const DatumVrijeme& vrijeme) : _klijent(klijent) { + _oznaka = AlocirajTekst(GenerisiOznaku(klijent.GetImePrezime().c_str(), redniBroj, vrijeme.GetGodina()).c_str()); + _uredjaj = AlocirajTekst(uredjaj); + _opisKvara = AlocirajTekst(opisKvara); + _redniBroj = redniBroj; + + _statusi.Dodaj(ZAPRIMLJEN, vrijeme); + } + + ServisniZahtjev(const ServisniZahtjev& other) : _klijent(other._klijent) { + _oznaka = AlocirajTekst(other.GetOznaka()); + _uredjaj = AlocirajTekst(other.GetUredjaj()); + _opisKvara = AlocirajTekst(other.GetOpisKvara()); + _redniBroj = other.GetRedniBroj(); + _intervencije = other.GetIntervencije(); + _statusi = other.GetStatusi(); + } + + bool DodajStatus(StatusZahtjeva status, const DatumVrijeme& vrijeme) { + DatumVrijeme posljednjeVrijeme = _statusi.GetElement2(_statusi.GetTrenutno() - 1); + StatusZahtjeva posljednjiStatus = GetTrenutniStatus(); + if (vrijeme > posljednjeVrijeme) { + if(posljednjiStatus == ZAVRSEN) { + return false; + } + else if ((int)status == (int)(posljednjiStatus + 1)) { + _statusi.Dodaj(status, vrijeme); + return true; + } + } + + return false; + } + + double UkupnaCijena() { + double sum = 0.0; + + for (const Intervencija& i : _intervencije) { + sum += i.GetCijena(); + } + + return sum; + } + + int UkupnoTrajanje() { + int sum = 0.0; + + for (const Intervencija& i : _intervencije) { + sum += i.GetTrajanjeMinuta(); + } + + return sum; + } + + + bool DodajIntervenciju(Intervencija intervencija) { + StatusZahtjeva trenutni = GetTrenutniStatus(); + if (trenutni == DIJAGNOSTIKA || trenutni == POPRAVKA) { + if (intervencija.GetCijena() > 0 && intervencija.GetTrajanjeMinuta() > 0) { + _intervencije.push_back(intervencija); + return true; + } + } + return false; + } + + +}; + + + +class Servis { + char* _naziv; + vector _zahtjevi; +public: + Servis(const char* naziv = "") { + _naziv = AlocirajTekst(naziv); + } + const char* GetNaziv() const { return _naziv; } + vector& GetZahtjevi() { return _zahtjevi; } + const vector& GetZahtjevi() const { + return + _zahtjevi; + } + ~Servis() { + delete[] _naziv; _naziv = nullptr; + } + + bool ZahtjevExist(int redniBroj) { + return any_of(_zahtjevi.begin(), _zahtjevi.end(), [&](const ServisniZahtjev& z) { + return z.GetRedniBroj() == redniBroj; + }); + } + bool ZahtjevExist(const char* oznaka) { + return any_of(_zahtjevi.begin(), _zahtjevi.end(), [&](const ServisniZahtjev& z) { + return strcmp(oznaka, z.GetOznaka()) == 0; + }); + } + + void DodajZahtjev(const ServisniZahtjev& zahtjev) { + if (ZahtjevExist(zahtjev.GetRedniBroj())) { + throw exception("zahtjev already exists"); + }; + if (ZahtjevExist(zahtjev.GetOznaka())) { + throw exception("zahtjev already exists"); + }; + _zahtjevi.push_back(zahtjev); + } + ServisniZahtjev* PronadjiZahtjev(string oznaka) { + + for (ServisniZahtjev& z : _zahtjevi) { + if (oznaka == z.GetOznaka()) { + return &z; + } + } + return nullptr; + } + + + bool EvidentirajStatus(string oznaka, StatusZahtjeva status, const DatumVrijeme& vrijeme) { + + ServisniZahtjev* zahtjev = PronadjiZahtjev(oznaka); + + if (!zahtjev) return false; + + if (zahtjev->DodajStatus(status, vrijeme)) { + thread t([&]() { + ostringstream buffer; + + buffer << " ---------------------------------------------------------------------------------" <GetKlijent().GetEmail() << endl; + buffer << "From: racuni@servis.ba" << endl; + buffer << "Subject: Servisni zahtjev zavrsen - racun" << endl; + buffer << "Postovani " << zahtjev->GetKlijent().GetImePrezime() << endl; + buffer << "Servisni zahtjev " << zahtjev->GetOznaka() << " za uredjaj " << zahtjev->GetUredjaj() << " je zavrsen." << endl; + buffer << "Ukupan iznos: " << zahtjev->UkupnaCijena() << " KM" << endl; + buffer << "Hvala na povjerenju." << endl; + + buffer << " ---------------------------------------------------------------------------------" << endl; + + cout << buffer.str(); + }); + t.join(); + return true; + } + + return false; + } + vector IzdvojiNezavrsene() { + vector nezavrseni; + + for (ServisniZahtjev& z : _zahtjevi) { + if (z.GetTrenutniStatus() != ZAVRSEN) { + nezavrseni.push_back(&z); + } + } + + return nezavrseni; + } + /* + IzracunajPrihod vraca zbir cijena svih intervencija evidentiranih + na + zahtjevima koji imaju status ZAVRSEN. Intervencije na nezavrsenim + zahtjevima ne ulaze u prihod. + */ + double IzracunajPrihod() { + double sum = 0.0; + + for (ServisniZahtjev& z : _zahtjevi) { + if (z.GetTrenutniStatus() == ZAVRSEN) { + sum += z.UkupnaCijena(); + } + } + return sum; + } + +}; +const char* GetOdgovorNaPrvoPitanje() { + cout << "Pitanje -> Pojasnite zbog cega se operator<< implementira kao globalna a ne funkcija clanica, te na koji nacin se implementiraju prefiksalni i postfiksalni oblik operatora inkrementiranja ? \n"; + return "Odgovor -> OVDJE UNESITE VAS ODGOVOR"; +} +const char* GetOdgovorNaDrugoPitanje() { + cout << "Pitanje -> Pojasnite nacin na koji biste, koristeci obradjene klase i metode, mogli saznati velicinu nekog tekstualnog fajla.\n"; + return "Odgovor -> OVDJE UNESITE VAS ODGOVOR"; +} +int main() { + /* + cout << PORUKA_TELEFON; cin.get(); + cout << PORUKA_TELEFON; cin.get(); + cout << PORUKA_ISPIT; cin.get(); + system("cls"); + cout << GetOdgovorNaPrvoPitanje() << crt; + cin.get(); + cout << GetOdgovorNaDrugoPitanje() << crt; + cin.get(); + Funkcija GenerisiOznaku generise oznaku servisnog zahtjeva u +formatu: + SRV-BBB/IN-GGGG + Potpis funkcije treba biti: + string GenerisiOznaku(const char* imePrezime, int redniBroj, int +godina) + SRV -> fiksni prefiks, + BBB -> redni broj zahtjeva popunjen nulama na slobodnim mjestima, + IN -> inicijali imena i prezimena klijenta, + GGGG -> godina zaprimanja zahtjeva. + Kod imena koje sadrzi vise rijeci koriste se inicijal prve i +posljednje + rijeci. Redni broj mora biti u rasponu 1-999, a godina 2000-2099. + Za neispravne podatke funkcija vraca "SRV-000/XX-0000". + */ + if (GenerisiOznaku("Amina Buric", 42, 2026) == "SRV-042/AB-2026") + cout << "Oznaka OK" << crt; + if (GenerisiOznaku("Goran Skondric", 7, 2026) == "SRV-007/GS2026") + cout << "Oznaka OK" << crt; + if (GenerisiOznaku("Ana Marija Kovac", 156, 2027) == + "SRV-156/AK-2027") + cout << "Oznaka OK" << crt; + if (GenerisiOznaku("Amina", 42, 2026) == "SRV-000/XX-0000" && + GenerisiOznaku("Amina Buric", 0, 2026) == "SRV-000/XX-0000" && + GenerisiOznaku("Amina Buric", 1000, 2026) == "SRV-000/XX-0000" + && + GenerisiOznaku("Amina Buric", 42, 1999) == "SRV-000/XX-0000") + cout << "Neispravni podaci za oznaku OK" << crt; + /* + Koristeci regex, funkcija ValidirajOznaku provjerava prethodno + definisani + format. Prefiks mora biti SRV, redni broj mora imati tri cifre i + ne moze + biti 000, inicijali moraju biti velika slova, a godina je 2000- + 2099. + Potpis funkcije treba biti: + bool ValidirajOznaku(const string& oznaka) + */ + if (ValidirajOznaku("SRV-042/AB-2026")) + cout << "OZNAKA VALIDNA" << crt; + if (!ValidirajOznaku("SRV/042-AB-2026") && + !ValidirajOznaku("SRV-42/AB-2026") && + !ValidirajOznaku("SRV-042/Ab-2026") && + !ValidirajOznaku("SRV-000/AB-2026") && + !ValidirajOznaku("2026-SRV-042/AB")) + cout << "OZNAKA NIJE VALIDNA" << crt; + Kolekcija brojevi; + brojevi.Dodaj(10, "Deset"); + brojevi.Dodaj(20, "Dvadeset"); + brojevi.Dodaj(10, "Deset"); + brojevi.Dodaj(30, "Trideset"); + brojevi.Dodaj(20, "Dvadeset"); + cout << brojevi << crt; + /* + UkloniDuplikate vraca novu kolekciju u kojoj je zadrzano samo prvo + pojavljivanje svakog para. Par predstavlja duplikat iskljucivo ako + su + jednaki i prvi i drugi element. Originalna kolekcija ostaje + nepromijenjena. + */ + Kolekcija bezDuplikata = + brojevi.UkloniDuplikate(); + cout << "Bez duplikata:" << crt << bezDuplikata; + cout << "Original:" << crt << brojevi; + try { + Kolekcija puna; + puna.Dodaj(1, "Jedan"); + puna.Dodaj(2, "Dva"); + puna.Dodaj(3, "Tri"); + } + catch (exception& e) { + cout << "Exception: " << e.what() << crt; + } + Kolekcija kopijaBrojeva = brojevi; + kopijaBrojeva[0] = 100; + Kolekcija dodijeljeniBrojevi; + dodijeljeniBrojevi = brojevi; + dodijeljeniBrojevi.GetElement2(0) = "Izmijenjeno"; + cout << "Original:" << crt << brojevi; + cout << "Kopija:" << crt << kopijaBrojeva; + cout << "Dodijeljeni objekat:" << crt << dodijeljeniBrojevi; + DatumVrijeme zaprimljen(9, 7, 2026, 8, 0); + DatumVrijeme dijagnostika(9, 7, 2026, 9, 0); + DatumVrijeme popravka(9, 7, 2026, 10, 0); + DatumVrijeme zavrsen(9, 7, 2026, 11, 0); + /* + ToString vraca datum i vrijeme u formatu DD.MM.GGGG HH:MM, + ukljucujuci + pocetne nule. + */ + cout << zaprimljen.ToString() << crt; // 09.07.2026 08:00 + if (dijagnostika > zaprimljen) + cout << "Vrijeme dijagnostike je nakon zaprimanja" << crt; + DatumVrijeme kopijaDatuma(dijagnostika); + if (kopijaDatuma == dijagnostika && !(zaprimljen == dijagnostika)) + cout << "Provjera vremena, OK." << crt; + Klijent amina("Amina Buric", "amina@fit.ba", "061-111-222"); + Klijent goran("Goran Skondric", "goran@fit.ba", "062-222-333"); + Klijent kopijaKlijenta = amina; + cout << kopijaKlijenta << crt; + /* + Intervencija sadrzi opis, ime tehnicara, cijenu i trajanje u + minutama. + */ + Intervencija pregled("Dijagnostika uredjaja", "Haris Hadzic", 20, + 30); + Intervencija zamjenaDiska("Zamjena diska", "Haris Hadzic", 85.5, + 45); + Intervencija instalacija("Instalacija sistema", "Maja Majic", 35, + 60); + /* ToString vraca podatke u formatu: + opis | tehnicar | cijena KM | trajanje min + Zamjena diska | Haris Hadzic | 85.50 KM | 45 min + */ + cout << zamjenaDiska.ToString() << crt; + /* + Konstruktor ServisnogZahtjeva generise oznaku na osnovu klijenta, + rednog broja i godine zaprimanja, te evidentira pocetni status + ZAPRIMLJEN + sa proslijedjenim vremenom. + */ + ServisniZahtjev laptop("Laptop", "Ne pokrece se", amina, 42, + zaprimljen); + ServisniZahtjev telefon("Telefon", "Razbijen ekran", goran, 7, + zaprimljen); + /* + ToString vraca podatke u formatu: + oznaka | ime i prezime klijenta | uredjaj | trenutni status + SRV-042/AB-2026 | Amina Buric | Laptop | ZAPRIMLJEN + */ + cout << laptop.ToString() << crt; + if (laptop.ToString() == + "SRV-042/AB-2026 | Amina Buric | Laptop | ZAPRIMLJEN") + cout << "ServisniZahtjev ToString OK" << crt; + /* + DodajStatus dodaje status samo ako je vrijeme vece od vremena + posljednjeg + statusa i ako status predstavlja neposredno sljedecu fazu. + Dozvoljen slijed je: ZAPRIMLJEN -> DIJAGNOSTIKA -> POPRAVKA -> + ZAVRSEN + Preskakanje ili ponavljanje statusa nije dozvoljeno. Nakon statusa + ZAVRSEN nisu dozvoljene nove promjene. Neuspjesan pokusaj vraca + false + bez izmjene kolekcije statusa. + */ + if (!laptop.DodajStatus(POPRAVKA, dijagnostika)) + cout << "Preskakanje statusa nije dozvoljeno" << crt; + if (laptop.DodajStatus(DIJAGNOSTIKA, dijagnostika)) + cout << "Status DIJAGNOSTIKA dodan" << crt; + if (!laptop.DodajStatus(DIJAGNOSTIKA, popravka)) + cout << "Ponavljanje statusa nije dozvoljeno" << crt; + /*DodajIntervenciju dodaje intervenciju samo dok je zahtjev u + statusu + DIJAGNOSTIKA ili POPRAVKA, uz cijenu i trajanje vece od nule. + Metoda vraca true ako je intervencija dodana, u suprotnom false. + UkupnaCijena vraca zbir cijena svih intervencija, a UkupnoTrajanje + zbir + njihovih trajanja u minutama. + */ + if (laptop.DodajIntervenciju(pregled)) + cout << "Intervencija dodana" << crt; + if (laptop.DodajStatus(POPRAVKA, popravka)) + cout << "Status POPRAVKA dodan" << crt; + laptop.DodajIntervenciju(zamjenaDiska); + laptop.DodajIntervenciju(instalacija); + cout << "Ukupna cijena: " << laptop.UkupnaCijena() << " KM" << + crt; + cout << "Ukupno trajanje: " << laptop.UkupnoTrajanje() << " min" + << crt; + ServisniZahtjev kopijaLaptopa = laptop; + cout << kopijaLaptopa << crt; + Servis fitServis("FIT Servis"); + /* + DodajZahtjev dodaje zahtjev u servis. Nije dozvoljeno dodati dva + zahtjeva + sa istim rednim brojem ili istom oznakom. U slucaju duplikata + metoda + baca izuzetak. + */ + fitServis.DodajZahtjev(laptop); + fitServis.DodajZahtjev(telefon); + try { + fitServis.DodajZahtjev(telefon); + } + catch (exception& e) { + cout << "Exception: " << e.what() << crt; + } + /* + PronadjiZahtjev vraca pokazivac na zahtjev sa proslijedjenom + oznakom. + Ako zahtjev nije pronadjen, metoda vraca nullptr. + */ + string oznakaLaptopa = laptop.GetOznaka(); + ServisniZahtjev* pronadjen = + fitServis.PronadjiZahtjev(oznakaLaptopa); + if (pronadjen != nullptr) + cout << "Pronadjen zahtjev: " << pronadjen->GetOznaka() << + crt; + if (fitServis.PronadjiZahtjev("SRV-999/XX-2026") == nullptr) + cout << "Zahtjev nije pronadjen" << crt; + /* + EvidentirajStatus pronalazi zahtjev i pokusava dodati status + koristeci + pravila metode DodajStatus. + Racun se salje u zasebnom threadu iskljucivo nakon uspjesnog + evidentiranja + statusa ZAVRSEN. Za ostale statuse se ne salju obavijesti. Ako + zahtjev ne + postoji ili status nije dodan, metoda vraca false. + Primjer sadrzaja racuna: + ------------------------------------------------------------------ + --------------- + To: amina@fit.ba + From: racuni@servis.ba + Subject: Servisni zahtjev zavrsen - racun + Postovani Amina Buric, + Servisni zahtjev SRV-042/AB-2026 za uredjaj Laptop je zavrsen. + Ukupan iznos: 140.50 KM + Hvala na povjerenju. + ------------------------------------------------------------------ + --------------- + */ + if (fitServis.EvidentirajStatus(oznakaLaptopa, ZAVRSEN, zavrsen)) + cout << "Zahtjev zavrsen i racun poslan" << crt; + if (!fitServis.EvidentirajStatus( + oznakaLaptopa, ZAVRSEN, DatumVrijeme(9, 7, 2026, 12, 0))) + cout << "Ponavljanje zavrsnog statusa nije dozvoljeno" << crt; + if (!fitServis.EvidentirajStatus( + "NEPOSTOJECI", DIJAGNOSTIKA, dijagnostika)) + cout << "Status nije evidentiran za nepostojeci zahtjev" << + crt; + + /* + IzdvojiNezavrsene vraca vector pokazivaca na sve zahtjeve ciji + trenutni + status nije ZAVRSEN. + */ + vector nezavrseni = + fitServis.IzdvojiNezavrsene(); + for (auto zahtjev : nezavrseni) + cout << zahtjev->GetOznaka() << " -> " + << StatusZahtjevaNazivi[(int)zahtjev->GetTrenutniStatus()] << + crt; + /* + IzracunajPrihod vraca zbir cijena svih intervencija evidentiranih + na + zahtjevima koji imaju status ZAVRSEN. Intervencije na nezavrsenim + zahtjevima ne ulaze u prihod. + */ + cout << "Ostvareni prihod: " << fitServis.IzracunajPrihod() + << " KM" << crt; + Servis kopijaServisa = fitServis; + cout << kopijaServisa.GetNaziv() << " ima " + << kopijaServisa.GetZahtjevi().size() << " zahtjeva" << crt; + cin.get(); + return 0; +} diff --git a/Ispiti/2026-07-09/README.md b/Ispiti/2026-07-09/README.md index 866a46b..b61677a 100644 --- a/Ispiti/2026-07-09/README.md +++ b/Ispiti/2026-07-09/README.md @@ -3,4 +3,10 @@ - enum VrstaDostave { STANDARDNA, BRZA, MEDJUNARODNA }; - enum StatusPosiljke { KREIRANA, PREUZETA, U_TRANZITU, ISPORUCENA, OTKAZANA }; - Kolekcija statički niz pointera (T* elementi[max]) -- string, vector, advanced regex, generisi oznaku +- string, vector, advanced regex, generisi oznaku, threadovi + +# Grupa 2 + +- enum StatusZahtjeva { ZAPRIMLJEN, DIJAGNOSTIKA, POPRAVKA, ZAVRSEN }; +- Kolekcija pointer na dinamicki niz (T* elementi) +- string, vector, advanced regex, generisi oznaku, threadovi From f363ce7ace108dac8498c6ec36ab4e6b5b1d3dbe Mon Sep 17 00:00:00 2001 From: null68 Date: Tue, 25 Aug 2026 01:30:34 +0200 Subject: [PATCH 6/6] fix: add answer to questions --- .../2026-07-09/PR2_2026_07_09_G2_Rje\305\241enje.cpp" | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git "a/Ispiti/2026-07-09/PR2_2026_07_09_G2_Rje\305\241enje.cpp" "b/Ispiti/2026-07-09/PR2_2026_07_09_G2_Rje\305\241enje.cpp" index 2b6f3d5..638dcbd 100644 --- "a/Ispiti/2026-07-09/PR2_2026_07_09_G2_Rje\305\241enje.cpp" +++ "b/Ispiti/2026-07-09/PR2_2026_07_09_G2_Rje\305\241enje.cpp" @@ -576,16 +576,18 @@ class Servis { } }; + const char* GetOdgovorNaPrvoPitanje() { cout << "Pitanje -> Pojasnite zbog cega se operator<< implementira kao globalna a ne funkcija clanica, te na koji nacin se implementiraju prefiksalni i postfiksalni oblik operatora inkrementiranja ? \n"; - return "Odgovor -> OVDJE UNESITE VAS ODGOVOR"; + return "Odgovor -> operator<< se implementira kao globalna funkcija jer lijevi operand nije objekat naše klase, a prefiksni i postfiksni ++ razlikuju se po parametru int kod postfiksnog oblika."; } + const char* GetOdgovorNaDrugoPitanje() { cout << "Pitanje -> Pojasnite nacin na koji biste, koristeci obradjene klase i metode, mogli saznati velicinu nekog tekstualnog fajla.\n"; - return "Odgovor -> OVDJE UNESITE VAS ODGOVOR"; + return "Odgovor -> Veličinu tekstualnog fajla možemo saznati otvaranjem fajla, pozicioniranjem na njegov kraj pomoću seekg() i očitavanjem pozicije pomoću tellg()"; } + int main() { - /* cout << PORUKA_TELEFON; cin.get(); cout << PORUKA_TELEFON; cin.get(); cout << PORUKA_ISPIT; cin.get(); @@ -594,6 +596,7 @@ int main() { cin.get(); cout << GetOdgovorNaDrugoPitanje() << crt; cin.get(); + /* Funkcija GenerisiOznaku generise oznaku servisnog zahtjeva u formatu: SRV-BBB/IN-GGGG