У четвртој години (трећа година учења предмета) бавимо се следећим темама:
Структуре података (стек и ред), Објектно оријентисано програмирање (увод и основни концепти), Windows програмирање (увод, основни концепти, основни концепти база података).
У овој години предмет предаје наставник Југослав Старчевић
Поставите питање наставнику ОВДЕ.
Објектно оријентисано програмирање (Object Oriented Programming, OOP) је релативно нови приступ у реализацији софтвера, настао као одговор на софтверску кризу, а који у основи има настојање да се реалан свет на природан начин моделује програмским кодом.
Објектно оријентисано програмирање заснива се на четири основна концепта:
Апстрактни типови података су један од основних концепата Објектно Оријентисаног Програмирања (ООП). Тај концепт практично значи да програмер, као корисник језика, може да дефинише неки свој тип који ће бити потпуно равноправан са свим другим, у језик уграђеним типовима. То је концепт који подржава идеју да се објекти и ентитети реалног света на што природнији и лакши начин моделују и представе у софтверском коду.
У програмском језику C++ апстрактни типови података се реализују преко класа (class). Класа је описана својим атрибутима (особинама, тј. пољима) и операцијама (акцијама, тј. функцијама).
Даље у овом тексту даћемо неколико примера дефиниције и коришћења класа. Почећемо са врло једноставним примерима, а затим ћемо их постепено "закомпликовати". Већина ових примера су урађени, или ће бити рађени на часовима вежби из Програмирања.
Такође, следећи линкови вам могу бити од користи:
http://sh.wikipedia.org/wiki/C%2B%2B
http://www.cplusplus.com/doc/tutorial/classes/
Погледајте и ове фајлове: klase.pdf , OOP.ppt
пример 1: једноставна класа
Ово је врло једноставан пример класе која има четири члана податка и једну методу (функцију чланицу). Сви чланови су јавни (декларисани у public делу класе).
модул ucenik
ucenik.h
#ifndef _ucenik.h
#define _ucenik.h
class TUcenik
{
public:
char Ime[100];
int GR; //godina rodjenja
int Godina; // koju godinu slusa
char Odeljenje[20];
void PredstaviSe(); // metoda, funkcija koja pripada klasi
};
#endif
ucenik.cpp
#include <iostream>
#include "ucenik.h"
void TUcenik::PredstaviSe()
{
cout<<"Ja sam "<<Ime
<<" rodjen "<<GR
<<". godine, trenutno sam "
<<Godina<<". godina, odeljenje "
<<Odeljenje<<endl;
}
main функција
#include <conio>
#include <iostream>
#include "ucenik.h"
main()
{
TUcenik u;
strcpy(u.Ime, "Petar Petrovic");
u.GR=1991;
u.Godina=4;
strcpy(u.Odeljenje, "IV-2");
u.PredstaviSe();
getch();
}
пример 2: енкапсулација
У овом примеру, који је проширење претходног, демонстрираћемо концепт енкапсулације. Енкапсулацијом (учаурењем, затварањем) се постиже да не буду сви чланови класе доступни за директан приступ, него да корисник (програмер који користи класу) мора да користи одговарајуће методе. Тиме се постиже да класа има контролу над вредностима које ће бити уписане у њене чланове податке.
Обратите пажњу на имплементацију метода set_GR и set_Godina.
модул ucenik
ucenik.h
class TUcenik
{
private:
char Ime[100];
int GR; //godina rodjenja
int Godina; // koju godinu slusa
char Odeljenje[20];
public:
void set_Ime(const char* const);
void set_GR(const int);
void set_Godina(const int);
void set_Odeljenje(const char* const);
void PredstaviSe(); // metoda, funkcija koja pripada klasi
};
ucenik.cpp
#include <iostream>
#include <string.h>
#include <DOS.H>
#include "ucenik.h"
void TUcenik::set_Ime(const char* const theIme)
{
strcpy(Ime, theIme);
}
void TUcenik::set_GR(const int theGR)
{
date d; // date je struktura deklarisana u DOS.H, pogledajte help
getdate(&d); // uzimanje sistemskog datuma, pogledajte help
if (((d.da_year-theGR)>=15) && ((d.da_year-theGR)<=20))
GR=theGR;
else
cout<<"Greska prilikom unosa Godine rodjenja. Ucenici moraju da imaju najmanje 15 godina, a najvise 20"<<endl;
}
void TUcenik::set_Godina(const int theGodina)
{
if (theGodina>=1 && theGodina<=4)
Godina=theGodina;
else
cout<<"Greska prilikom unosa Godine. Isparavna vrednost je od 1 do 4" <<endl;
}
void TUcenik::set_Odeljenje(const char* const theOdeljenje)
{
strcpy(Odeljenje, theOdeljenje);
}
void TUcenik::PredstaviSe()
{
cout<<"Ja sam "<<Ime
<<" rodjen "<<GR
<<". godine, trenutno sam "
<<Godina<<". godina, odeljenje "
<<Odeljenje<<endl;
}
main функција
#include <conio>
#include <iostream>
#include "ucenik.h"
main()
{
TUcenik u;
u.set_Ime("Petar Petrovic");
u.set_GR(1955); // namerno pogresno, starija osoba
u.set_GR(2003); // namerno pogresno, mladja osoba
u.set_GR(1993); // ispravno
u.set_Godina(10); // namerno pogresno
u.set_Godina(4); // ispravno
u.set_Odeljenje("IV-2");
u.PredstaviSe();
getch();
}
пример 3: конструктори и деструктори (constructor, destructor)
Конструктори и деструктори су специјалне методе (функције чланице) класе који се имплицитно позивају на почетку (конструктор) и на крају (деструктор) животног века објекта неке класе. Имају потпуно исто име као и класа (деструктор има знак ~ испред имена) и не смеју да враћају никакав тип, чак ни void. Декларисање конструктора или деструктора може да се изостави, али у том случају сам преводилац ће генерисати тзв. default constructor и/или default destructor.
Намена конструктора је да "конструише" објекат, тј. да изврши све што је неопходно да би се објекат неке класе, на почетку његовог "живота", довео у "радно" стање. То може бити, на пример, иницијализација неких унутрашњих чланова података, заузимање меморије за неке потребе објекта и слично. Конструктор се може и преклопити (overload), што практично значи да класа може да има више конструктора, о чему ћемо у неком од следећих примера.
Намена деструктора је управо супротна намени конструктора. Он мора да "почисти" за објектом, односно да изврши све што је потребно урадити када објекат престаје да "живи". На пример, да ослободи меморију коју је конструктор заузео на почетку и слично.
У следећем, релативно једноставном али ипак нешто сложенијем примеру од претходних, ћемо показати како функционишу конструктори и деструктори, а биће показан и један нови однос између објеката две класе, у коме објекат једне класе уме да "користи" објекат друге класе.
Погледајте и анализирајте пажљиво овај пример.
модул особа
osoba.h
#ifndef _osoba.h
#define _osoba.h
//////////////////////
// deklaracija klase
//////////////////////
class TOsoba
{
// sve sto nije deklarisano pod public:
// smatra se po "default"-u private:
char* Ime;
char* Prezime;
public: // javne metode
TOsoba(const char* const, const char* const); // konstruktor
~TOsoba(); // destruktor
void PredstaviSe();
};
#endif
osoba.cpp
#include <iostream>
#include <string>
#include "osoba.h"
///////////////////////////
// implementacija metoda klase
////////////////////////
// konstruktor
TOsoba::TOsoba(const char* const theIme, const char* const thePrezime)
{
Ime = new char[100];
Prezime = new char[100];
strcpy(Ime, theIme);
strcpy(Prezime, thePrezime);
}
// destruktor
TOsoba::~TOsoba()
{
cout<<"Ubila se osoba "<<Ime<<" "<<Prezime<<endl;
delete[] Ime;
delete[] Prezime;
}
void TOsoba::PredstaviSe()
{
cout << "Ja sam osoba " << Ime << " " << Prezime << endl;
}
///////////////////////////////////////
// kraj implementacije klase
//////////////////////////////////////
модул полицајац
policajac.h
#ifndef _policajac.h
#define _policajac.h
#include "osoba.h"
class TPolicajac
{
char* Ime;
char* Znacka;
public:
TPolicajac(const char* const, const char* const);
~TPolicajac();
void Legitimisi(TOsoba*);
};
#endif
policajac.cpp
#include "policajac.h"
TPolicajac::TPolicajac(const char* const theIme, const char* const theZnacka)
{
Ime = new char[100];
Znacka = new char[100];
strcpy(Ime, theIme);
strcpy(Znacka, theZnacka);
}
TPolicajac::~TPolicajac()
{
cout<<"Ubio se policajac "<<Ime<<" "<<Znacka<<endl;
delete[] Ime;
delete[] Znacka;
}
void TPolicajac::Legitimisi(TOsoba* o)
{
cout << "Ja sam policajac " << Ime <<" (" << Znacka << "). ";
cout << "Predstavi se!" << endl;
o->PredstaviSe();
cout << endl;
}
main функција
#include <conio>
#include <iostream>
// moji headeri uvek treba da idu posle standardnih
#include "osoba.h"
#include "policajac.h"
main()
{
// staticko koriscenje klase
TOsoba p("Pera", "Peric"),
m("Mile", "Miletic"); // deklaracija i poziv konstruktora
TPolicajac r("Bambusic", "332344");
cout<<"STATICKI OBJEKTI:"<<endl;
r.Legitimisi(&p);
r.Legitimisi(&m);
// dinamicko koriscenje klase
// deklaracija objekta, zauzimanje memorije za objekat (new) i poziv konstruktora
TOsoba *pp = new TOsoba("Marko", "Markovic"),
*pm = new TOsoba("Kosta", "Kostic");
TPolicajac *pr = new TPolicajac("Bambalic", "534583");
cout<<"DINAMICKI OBJEKTI"<<endl;
pr->Legitimisi(pp);
pr->Legitimisi(pm);
cout<<"brisanje dinamickih objekata, implicitni poziv destruktora"<<endl;
delete pp;
delete pm;
delete pr;
getch();
}
пример 4: преклопљени оператори, вишеструки конструктори
модул komplex
komplex.h
#ifndef _komplex.h
#define _komplex.h
class TComplex
{
double real;
double imaginar;
public:
TComplex(); // konstruktor
TComplex(double r, double i); // konstruktor
void set_r(double r);
double get_r() const; // inspektor
void set_i(double i);
double get_i() const;// inspektor
TComplex& operator=(TComplex c);
TComplex operator+(TComplex c);
TComplex operator-(TComplex c);
TComplex operator*(TComplex c);
TComplex operator/(TComplex c);
TComplex& operator++();
TComplex& operator--();
};
#endif
komplex.cpp
#include "komplex.h"
TComplex::TComplex(): real(0), imaginar(0)
{
}
TComplex::TComplex(double r, double i): real(r), imaginar(i)
{
}
void TComplex::set_r(double r)
{
real=r;
}
double TComplex::get_r() const
{
return real;
}
void TComplex::set_i(double i)
{
imaginar=i;
}
double TComplex::get_i() const
{
return imaginar;
}
TComplex& TComplex::operator=(TComplex c)
{
real=c.get_r();
imaginar=c.get_i();
return *this;
}
TComplex TComplex::operator+(TComplex c)
{
TComplex rezultat;
rezultat.set_r(real+c.get_r());
rezultat.set_i(imaginar+c.get_i());
return rezultat;
}
TComplex TComplex::operator-(TComplex c)
{
TComplex rezultat;
rezultat.set_r(real-c.get_r());
rezultat.set_i(imaginar-c.get_i());
return rezultat;
}
TComplex TComplex::operator*(TComplex c)
{
TComplex rezultat;
rezultat.set_r(real*c.get_r());
rezultat.set_i(imaginar*c.get_i());
return rezultat;
}
TComplex TComplex::operator/(TComplex c)
{
TComplex rezultat;
rezultat.set_r(real/c.get_r());
rezultat.set_i(imaginar/c.get_i());
return rezultat;
}
TComplex& TComplex::operator++()
{
real++;
imaginar++;
return *this;
}
TComplex& TComplex::operator--()
{
real--;
imaginar--;
return *this;
}
main функција
#include <conio>
#include <iostream>
#include "komplex.h"
main()
{
TComplex c, c1(1,1), c2(3,4);
cout<<"c real: "<<c.get_r()<< "<span="" data-mce-bogus="1" class="mceItemHidden mceItemNbsp"> imag: "<
cout<<"c1 real: "<<c1.get_r()<< "<span="" data-mce-bogus="1" class="mceItemHidden mceItemNbsp"> imag: "<
cout<<"c2 real: "<<c2.get_r()<< "<span="" data-mce-bogus="1" class="mceItemHidden mceItemNbsp"> imag: "<
cout<<endl;
c=c1+c2;
cout<<"c=c1+c2 real: "<<c.get_r()<< "<span="" data-mce-bogus="1" class="mceItemHidden mceItemNbsp"> imag: "<
c=c1-c2;
cout<<"c=c1-c2 real: "<<c.get_r()<< "<span="" data-mce-bogus="1" class="mceItemHidden mceItemNbsp"> imag: "<
c=c1*c2;
cout<<"c=c1*c2 real: "<<c.get_r()<< "<span="" data-mce-bogus="1" class="mceItemHidden mceItemNbsp"> imag: "<
c=c1/c2;
cout<<"c=c1/c2 real: "<<c.get_r()<< "<span="" data-mce-bogus="1" class="mceItemHidden mceItemNbsp"> imag: "<
c++;
cout<<"c++ real: "<<c.get_r()<< "<span="" data-mce-bogus="1" class="mceItemHidden mceItemNbsp"> imag: "<
getch();
}
Наслеђивање је концепт ООП-а који (уз полиморфизам) омогућава тзв. code reusability, односно поновно коришћење кода. То је концепт који у многоме повећава продуктивност програмера.
Наслеђивање је релација (однос) између класа. На пример, ако класа Б наслеђује класу А то значи:
Наслеђивање се у језику C++ реализује "извођењем класа". Извођење није исто што и наслеђивање, већ је шири појам. То значи да није свако извођење истовремено и наслеђивање.
пример:
class A
{
public:
void f();
};
class B: public A
{
public:
void g();
};
У горњем примеру јавно смо извели класу Б из класе А (class B: public A) што је једино извођење које је истовремено и наслеђивање. Постоје и тзв protected и private извођења, која нису наслеђивање зато јер у том случају однос између класа А и Б није такав да је Б "једна врста" А, него је класа Б класа која "садржи" класу А. Овим врстама извођења се нећемо бавити, јер се користе релативно ретко.
У примеру који смо дали, класа А има једну методу f() а класа Б која је наследила А има наслеђену методу f() и још додатну своју методу g().
Класе наследнице наслеђују и "виде" све делове родитељске класе који нису у private секцији (значи public и protected). Конструктори и деструктори (види овде) се НЕ наслеђују. Приликом креирања објекта класе наследнице, прво се извршавају конструктори њене родитељске класе па тек онда конструктор те класе. Код деструктора је супротно: прво се активира деструктор наследнице, па затим деструктор родитеља.
Још један, мало већи пример наслеђивања можете да преузмете овде.
Проверите своје знање из области структура података стек и ред, као и из Увода у ООП.
Тестови НИСУ за оцену и број поена које освојите НЕЋЕ на њу утицати, али на оцену ЋЕ утицати ПОЗИТИВНО ако тестове урадите!
Стек, ред тест: https://goo.gl/forms/xxa5qkdH7HuF6vAu1
Увод у ООП тест: https://goo.gl/forms/GlW5KemWk3JaU1KR2
За израду завршног пројекта из програмирања од користи вам може бити следеће:
PMOV.pdf - Скрипта помоћу које ћете лакше израдити модел информационог система. Прочитати поглавље 4.
projekat.zip - Шаблон Word документа који ће вам олакшати израду пројектне документације.
complex_preklopljeni_operatori.zip
У овом примеру можете да погледате: