OOP C++ - programiranje

Download Report

Transcript OOP C++ - programiranje

Objektno orijentirano programiranje
http://www.oss.unist.hr/~ncaklovi/oop
Nenad Čaklović
[email protected]
1. predavanje
• odlike objektnog programiranja
• uvod u programski jezik C++
• razlike C i C++
Objektno programiranje
•
problemi proceduralnog programiranja:
–
–
–
•
odlike objektno orijentiranog programiranja:
–
–
–
–
–
–
•
teško održavanje velikih projekata
osjetljivost na promjene u implementaciji
nemogućnost dodavanja novih tipova i redefinicije operatora
apstrakcija podataka (data abstraction)
enkapsulacija (encapsulation)
skrivanje podataka (data hiding)
polimorfizam (polymorphism)
dinamičko povezivanje (dynamic binding)
nasljeđivanje (inheritance)
pravila “čistog” objektnog programiranja
–
–
–
–
–
sve je objekt
program je hrpa objekata koji komuniciraju porukama
svaki objekt ima svoje podatke sastavljene od drugih objekata
svaki objekt ima tip
svi objekti istog tipa mogu primati iste poruke
Programski jezik C++
•
•
•
•
•
1979. Bjarne Stroustrup u Bellu (sadašnji AT&T) - “C s klasama”
1983. C++, dodatna proširenja
1989. osnovan ANSI komitet za standardizaciju C++ na osnovu ARM-a
(Annotated C++ Reference Manual)
1998. usvojen ISO standard
još uvijek traje petogodišnji moratorij na promjene standarda
•
literatura:
–
•
usenet:
–
–
•
Bjarne Stroustrup: The C++ Programming Language
comp.lang.c++
comp.std.c++
C++ se može koristiti i samo kao bolji C
void
• ne mora se pisati void kao argument funkcije
C
C++
int main(void){
return 0;
}
int main(){
}
komentari
• jednolinijski komentari, počinju sa //
C
C++
/* returns square */
int f(int n){
/* int i, s=0;
for(i=0; i<n; ++i)
s += n;
return s;
*/
return n*n; /* easier */
}
// returns square
int f(int n){
/* int s=0;
for(int i=0; i<n; ++i)
s += n;
return s;
*/
return n*n; // much easier
}
deklaracija varijabli
• varijable se mogu deklarirati bilo gdje unutar funkcije
C
C++
int f(char* s, int n){
int i, c = 0;
char* p = s;
if(!p)
return 0;
for(i=0; i<n; ++i)
if(p[i] == 'a')
++c;
return c;
}
int f(char* s, int n){
char* p = s;
if(!p)
return 0;
int c = 0;
for(int i=0; i<n; ++i)
if(p[i] == 'a')
++c;
return c;
}
bool
• novi tip, može imati vrijednosti true i false
C
C++
int f(int n)
{
if(n < 0)
return 1;
return 0;
}
bool f(int n)
{
if(n < 0)
return true;
return false;
}
int main(void)
{
int x = -3;
int b = f(x);
if(b)
x = abs(x)
return 0;
}
int main()
{
int x = -3;
bool b = f(x);
if(b)
x = abs(x);
}
operator ::
• omogućava pristup skrivenim globalnim varijablama
C
C++
int n;
int n;
int main(void)
{
int n;
for(n=0; n<10; ++n)
n++;
return 0;
}
int main()
{
for(int n=0; n<10; ++n)
::n++;
}
pretpostavljene vrijednosti argumenata
• zadnji argumenti funkcije mogu imati pretpostavljenu (default) vrijednost
C
C++
int f(int n, int b)
{
return n % b == 0;
}
bool f(int n, int b = 2)
{
return n % b == 0;
}
int main(void)
{
f(10, 5);
f(9, 3);
f(8, 2);
return 0;
}
int main()
{
f(10, 5);
f(9, 3);
f(8);
}
overloading funkcija
• mogu postojati funkcije istog imena, različitih tipova argumenata
C
C++
#include <stdio.h>
#include <cstdio>
void print_int(int n)
{
printf(“%d\n”, n);
}
void print(int n)
{
printf(“%d\n”, n);
}
void print_double(double n)
{
printf(“%lf\n”, n);
}
void print(double n)
{
printf(“%lf\n”, n);
}
int main(void)
{
print_int(10);
print_double(3.14);
return 0;
}
int main()
{
print(10);
print(3.14);
}
reference
• T& je referenca na tip T
C
C++
void swap(int* a, int* b)
{
int t;
if(!a || !b)
return;
t = *a;
*a = *b;
*b = t;
}
void swap(int& a, int& b)
{
int t = a;
a = b;
b = t;
}
int main(void)
{
int x=5, y=7;
swap(&x, &y);
return 0;
}
int main()
{
int x=5, y=7;
swap(x, y);
}
reference
•
referenca mora biti inicijalizirana
double d = 1.25;
double& r = d;
•
referenca se ne može promijeniti, sve operacije na referenci se odnose na
varijablu na koju referenca pokazuje
int n = 1;
int& r = n;
r = 3; // mijenja vrijednost varijable n
•
preko konstantne reference ne može se mijenjati ni varijabla na koju referenca
pokazuje
int n = 1;
const int& r = n;
r = 3; // compile error
“Hello world!”
#include <iostream>
int main()
{
std::cout << "Hello world!" << std::endl;
}
2. predavanje
• objekti
• klase
Objekti
•objekt: podaci + operacije nad podacima
objekt
// date.h
struct
int
int
int
};
void
void
void
void
date {
year;
month;
day;
set_date(struct date*, int y, int m, int d);
current_date(struct date*);
print_date(const struct date*);
next_date(struct date*);
C implementacija
// date.c
#include <stdio.h>
#include "date.h"
void set_date(struct date* dt, int y, int m, int d){
dt->year = y; dt->month = m; dt->day = d;
}
void current_date(struct date* dt){
dt->year = 2002; dt->month = 4; dt->day = 19;
}
void print_date(const struct date* dt){
printf("%02d.%02d.%d.", dt->day, dt->month, dt->year);
}
void next_date(struct date* dt){ // oversimplified
dt->day++;
if(dt->day > 30){ dt->day -= 30; dt->month++; }
if(dt->month > 12){ dt->month -= 12; dt->year++;}
}
funkcije članovi
// date.h revisioned
struct
int
int
int
void
void
void
void
void
date {
year;
month;
day;
};
set(int y, int m, int d);
get(int* y, int* m, int* d) const;
current();
print() const;
next();
C++ implementacija
// date.cpp
#include <iostream>
#include "date.h"
void date::set(int y, int m, int d){
year = y; month = m; day = d;
}
void date::get(int* py, int* pm, int* pd) const {
*py = year; *pm = month; *pd = day;
}
void date::print() const {
std::cout << day << '.' << month << '.' << year << '.';
}
void date::next(){ // oversimplified
day++;
if(day > 30){ day -= 30; month++; }
if(month > 12){ month -= 12; year++;}
}
upotreba objekata
• upotreba strukture iz C i C++
C
C++
int main(void)
{
struct date d;
current_date(&d);
next_date(&d);
print_date(&d);
return 0;
}
int main()
{
date d;
d.current();
d.next();
d.print();
}
class
// date.h rerevisioned
// date.h rerevisioned
struct date {
private:
int year;
int month;
int day;
public:
void set(int y, int m,
int d);
void get(int* y, int* m,
int* d) const;
void current();
void print() const;
void next();
};
class date {
int year;
int month;
int day;
public:
void set(int y, int m,
int d);
void get(int* y, int* m,
int* d) const;
void current();
void print() const;
void next();
};
private, public
• samo objekt smije pristupati svojim privatnim članovima
class X {
int n;
bool test();
public:
void work(X& other);
};
int main(){
X x1, x2;
int n = x1.n; // error
if(x1.test()) // error
return 1;
x1.work(x2);
// OK
return 0;
}
private, public
• samo objekt i drugi objekti istog tipa smiju pristupati privatnim članovima objekta
class X {
int n;
bool test();
public:
void work(X& other);
};
// all OK
void X::work(X& other)
{
if(!test())
return;
if(other.test())
n = other.n;
}
const funkcije
• const funkcija član ne može mijenjati objekt
class integer {
int n;
public:
int read() const;
void write(int i);
};
// 1st version, all OK
int integer::read() const { return n; }
void integer::write(int i) { n = i; }
// 2nd version, illegal
int integer::read() const {
if(n < 0) n = 0; // error!
return n;
}
const funkcije
• na konstantnim referencama se mogu zvati samo konstantne funkcije
void f(integer& x, const integer& y)
{
int a = x.read(); // OK
x.write(5);
// OK
int b = y.read(); // OK
y.write(8);
// error!
};
inline funkcije
• definicija funkcije člana se može pisati u tijelu klase
class integer {
int n;
public:
int read() const { return n; }
void write(int i) { n = i; }
};
class integer {
int n;
public:
int read() const;
void write(int i);
};
inline int integer::read() const { return n; }
inline void integer::write(int i) { n = i; }
this
• za svaku klasu T postoji skriveni const pointer: T* const this
class integer {
int n;
public:
int read() const { return n; }
void write(int i) { n = i; }
};
class integer {
int n;
public:
int read() const { return this->n; }
void write(int i) { this->n = i; }
};
this
• koristi se i kada iz funkcije člana treba poslati pointer ili referencu na objekt
void print1(date* dt){
int d, m, y; dt->get(&d, &m, &y);
std::cout << d << '.' << m << '.' << y << '.';
}
void print2(const date& dt){
int d, m, y; dt.get(&d, &m, &y);
std::cout << d << '.' << m << '.' << y << '.';
}
void date::print() const {
::print1(this);
::print2(*this);
}
3. predavanje
• stvaranje objekata
• inicijalizacija objekata
dinamička alokacija u C++
• operatori new, delete i delete[]
int* pn = new int;
float* pf = new float;
date* pd = new date;
delete pn;
delete pf;
delete pd;
pn = 0;
pf = 0;
pd = 0;
int* pn = new int[15];
date* pd = new date[3];
delete[] pn; pn = 0;
delete[] pd; pd = 0;
konstruktor, destruktor
•
posebne funkcije članovi, nemaju tip povratne vrijednosti
•
konstruktor:
–
–
•
za klasu T naziv funkcije je T()
poziva se pri stvaranju objekta
destruktor
–
–
za klasu T naziv funkcije je ~T()
poziva se pri uništenju objekta
konstruktor, destruktor
class screen {
int width, height;
public:
screen();
~screen();
bool draw(const char* txt);
};
screen::screen() {
width = 1024;
height = 768;
::fill_screen(1);
}
screen::~screen(){
::fill_screen(0);
}
int main(){
screen s;
s.draw("Hello!");
}
int main(){
screen* ps = new screen;
ps->draw("Hello!");
delete ps;
}
konstruktor sa argumentima
class screen {
int width, height;
public:
screen(int width, int height);
~screen();
bool draw(const char* txt);
};
screen::screen(int w, int h) { width = w; height = h; }
screen::screen(int width, int height) {
this->width = width; this->height = height; }
screen::screen(int w, int h) : width(w), height(h) { }
screen::screen(int width, int height) :
width(width), height(height) { }
konstruktor sa argumentima
int main(){
screen s(1024, 768);
s.draw("Hello again!");
}
• i built-in tipovi imaju konstruktor
int width(1024), height(768);
double r(7.24);
bool f(false);
int* pn = new int(42);
unsigned long* pl = new unsigned long(666);
primjer: čitanje iz datoteke (C)
int main(void){
char head[40], data[256];
FILE* fp = fopen("a.bmp", "r");
if(!fp)
return 1;
if(!fread(head, 40, fp)){
fclose(fp);
return 2;
}
if(!fread(data, 256, fp)){
fclose(fp);
return 3;
}
show_picture(data);
fclose(fp);
return 0;
}
primjer: čitanje iz datoteke (C++)
int main(){
char head[40], data[256];
file f("a.bmp");
if(!f.open())
return 1;
if(!f.read(head, 40))
return 2;
if(!f.read(data, 256))
return 3;
::show_picture(data);
return 0;
}
class file {
FILE* fp;
public:
file(const char*);
~file();
bool open();
int read(void*, int);
};
file::file(const char* name){
fp = ::fopen(name, "r");
}
file::~file() {
if(fp) ::fclose(fp);
}
...
default konstruktor
• konstruktor koji se može pozvati bez argumenata
• kada klasa nema konstruktora, generira se default konstruktor
• kada pravimo polja objekata, objekt mora imati default konstruktor
class point {
int x, y;
public:
point() : x(0), y(0) {}
};
int main(){
point pts[8];
return 0;
}
class point {
int x, y;
public:
point(int x=0, int y=0)
: x(x), y(y) {}
};
int main(){
point* p = new point[8];
…
delete[] p;
}
copy konstruktor
• konstruktor koji kao prvi argument prima referencu na drugi objekt istog tipa
class point {
int x, y;
public:
point(int x, int y);
point(const point& other);
};
point::point(int ix, int iy) : x(ix), y(iy) {}
point::point(const point& other) : x(other.x), y(other.y) {}
int main(){
point p1(1, 0);
point p2(p1);
}
inicijalizacija
• članovi se inicijaliziraju redoslijedom kojim su navedeni u tijelu klase
class point {
int x, y;
public:
point(int x, int y);
};
point::point(int ix, int iy) : y(iy), x(ix) { }
class objekti kao članovi
• prvo se pozivaju konstruktori članova, a zatim konstruktor objekta
class interval {
point start;
point end;
public:
interval(int x1, int y1, int x2, int y2);
interval(const point& p1, const point& p2);
};
interval::interval(int x1, int y1, int x2, int y2) :
start(x1, y1), end(x2, y2) { }
interval::interval (const point& p1, const point& p2) :
start(p1), end(p2) { }
4. predavanje
• nasljeđivanje
statički članovi (static members)
• dijele ih svi objekti istog tipa
• ekvivalent globalnih varijabli/funkcija u C-u
// track.h
class track {
static int numobj; // static member
public:
track();
~track();
static void print_name(); // static member function
};
// track.cpp
int track::numobj = 0;
track::track() { ++numobj; }
track::~track() { --numobj; }
void track::print_name(){ std::cout << "track"; }
operator::
• može se pisati pri pristupu članovima objekta
• obavezan pri pristupu statičkim članovima
class point {
int x, y;
public:
double length();
};
double point::length(){ return sqrt(x*x + y*y); }
int main(){
point p;
double n1 = p.point::length();
// isto kao p.length()
point* pp = &p;
double n2 = pp->point::length(); // isto kao pp->length()
}
namespace
• grupiranje globalnih simbola (funkcija, tipova i varijabli)
// oop.h
namespace oop
{
class point { public: int x, y; };
double distance(const point& p1, const point& p2);
}
// oop.cpp
double oop::distance(const point& p1, const point& p2){
int a(p1.x-p2.x), b(p1.y-p2.y);
return sqrt(a*a + b*b);
}
namespace
• korištenje simbola - operator:: ili 'using namespace'
int main(){
oop::point p1, p2;
double len = oop::distance(p1, p2);
}
int main(){
using namespace oop;
point p1, p2;
double len = distance(p1, p2);
}
std namespace
• std::cin, std::cout, std::string
#include <iostream>
#include <string>
int main(){
using namespace std;
string s; cin >> s;
cout << s << " ima " << s.length() << " znakova." << endl;
char c; cin >> c;
int pos = s.find(c);
if(pos >= 0)
cout << "slovo " << c << " se prvi puta pojavljuje na
" << pos << ". mjestu" << endl;
}
nasljeđivanje
class person {
string name;
date birth;
string address;
...
};
class student {
string name;
date birth;
string address;
short schoolYear;
float averageMark;
...
};
class student {
person p;
short schoolYear;
float averageMark;
...
};
class student : person {
short schoolYear;
float averageMark;
...
};
bazna klasa, izvedena klasa
•
izvođenje ili nasljeđivanje
person
student
•
- bazna klasa (base class, superclass)
- izvedena klasa (derived class, subclass)
built-in tip ne može biti bazna klasa
poziv funkcija bazne klase
• funkcije članovi bazne klase se pozivaju preko operatora::
void person::print(){
std::cout << name << birth << address;
}
void student::report(){
person::print();
std::cout << schoolYear << averageMark;
}
protected
• objekti izvedenih klasa smiju pristupati protected članovima
class person {
protected:
string name;
date birth;
string address;
};
class student : public person {
short schoolYear;
float averageMark;
public:
bool check_status(){
return schoolYear < 2 && birth.year < 1980;
}
};
upcasting
• objekt izvedene klase se može tretirati kao objekt bazne klase
preko reference ili pointera
int main(){
person p;
student s;
person* p1 = &p; // ili
student* s1 = &s; // ili
person& p1 = p;
student& s1 = s;
person* p2 = &s; // OK, svaki 'student' je 'person'
person& p3 = s; // OK
student* s2 = &p; // error! -> p nije student
student& s3 = p; // error!
student* s4 = p2; // error! (iako p2 pokazuje na studenta)
}
private, protected izvođenje
• privatno izvođenje skriva članove bazne klase
• protected izvođenje skriva članove bazne klase, ali ne od izvedenih klasa
class A {
public:
int i;
};
class B : protected A {};
class B : private A {};
class C : public B {
public:
void f() { i = 0; }
};
int main(){
B b;
b.i = 1; // error!
}
int main(){
C c;
c.i = 1; // error!
}
inicijalizacija
1. konstruktor bazne klase
2. konstruktori članova
3. konstruktor objekta
person::person(const string& n, int y, int m, int d, const
string& a) : name(n), date(y, m, d), address(a) {}
student::student(const string& n, int y, int m, int d,
const string& a, short sy) : person(n, y, m, d, a),
schoolYear(sy) {}
• destruktori se pozivaju obrnutim redom
skrivanje funkcija
• istoimena funkcija u izvedenoj klasi skriva funkciju u baznoj klasi
class person {
public:
void print();
};
class student : public person {
public:
void print();
};
int main(){
student s;
s.print();
s.person::print();
}
virtualne funkcije
• virtualna funkcija se može pregaziti (override) u izvedenoj klasi
• poziv funkcije je poziv u zadnjoj izvedenoj klasi koja je pregazila funkciju
(osim u konstruktoru i destruktoru)
class instrument {
public:
virtual void play(){}
};
int main (){
instrument i;
piano p;
drum d;
class drum : public instrument {
public:
void play(){
cout << "dum, dum";
}
};
class piano : public instrument {
public:
void play(){
cout << "pling";
}
};
instrument* pi = &i;
pi->play(); // -
}
pi = &p;
pi->play();
// pling
pi = &d;
pi->play();
// dum, dum
5. predavanje
• virtualne funkcije
• višestruko nasljeđivanje
mehanizam virtualnih funkcija
• veličina objekta naraste za 4 byte-a čim ima barem jednu virtualnu funkciju
• pointer (vptr) na tablicu virtualnih funkcija (vtable)
class virt0 {
int n;
public:
void f(){}
};
int main (){
using namespace std;
virt0 v0;
cout << sizeof(v0); // 4
class virt1 {
int n;
public:
virtual void f(){}
};
class virt2 {
int n;
public:
virtual void f(){}
virtual void g(){}
};
virt1 v1;
cout << sizeof(v1); // 8
virt2 v2;
cout << sizeof(v2); // 8
}
mehanizam virtualnih funkcija
class virt2 {
int n;
public:
virtual void f(){}
virtual void g(){}
};
a
vptr
n
vtable
b
int main(){
virt2 a, b;
}
vptr
n
• tablica virtualnih funkcija vrijedi za sve objekte klase
• tablica virtualnih funkcija se inicijalizira u konstruktoru
• sve funkcije osim konstruktora mogu biti virtualne
f()
g()
virtualni destruktor
• obavezan ako će se uništavanje objekata vršiti preko pointera na baznu klasu
int main (){
instrument* pins[] = {
new instrument,
new piano,
new drum,
};
for(int i=0; i<3; ++i)
pins[i]->play();
for(int j=0; j<3; ++j)
delete pins[j];
}
class instrument {
public:
virtual void play() {}
virtual ~instrument() {}
};
čisto virtualne funkcije (pure virtual functions)
• čisto virtualne funkcije se označavaju sa =0 u deklaraciji
class instrument {
public:
virtual void play() = 0;
};
• čisto virtualne funkcije mogu imati definiciju
class instrument {
public:
virtual void play() = 0;
};
void instrument::play(){
cout << "moj zvuk: ";
}
class piano :
public instrument {
public:
virtual void play();
};
void piano::play(){
instrument::play();
cout << "pling" << endl;
}
apstraktna klasa (abstract class)
• klasa sa bar jednom čisto virtualnom funkcijom
• nije moguće napraviti objekt apstraktne klase, koriste se pointeri i reference na njih
• čisto virtualni destruktor mora imati definiciju
class instrument {
public:
virtual void play() = 0;
virtual void tune() = 0;
virtual ~instrument() = 0;
};
instrument::~instrument() {}
apstraktna klasa samo sa čisto virtualnim funkcijama =
čisto apstraktna (pure abstract) klasa =
interface
višestruko nasljeđivanje (multiple inheritance)
• izvedena klasa ima više baznih klasa
class phone {
public:
virtual void call(int);
virtual void hangup();
virtual void redial();
};
class device {
public:
virtual void on();
virtual void off();
};
class moveable {
protected:
point GPScoord;
public:
virtual point coords();
};
class mobile_phone :
public phone,
public device,
public moveable
{
};
int main(){
mobile_phone mp;
mp.on();
mp.call(238729);
point p = mp.coords();
}
inicijalizacija
1. konstruktori baznih klasa, redom kojim su bazne klase navedene
2. konstruktori članova
3. konstruktor objekta
A
B
class A {
public:
A(){ cout << "A"; }
~A(){ cout << "~A"; }
};
class B {
public:
B(){ cout << "B"; }
~B(){ cout << "~B"; }
};
class M {
public:
M(){ cout << "M"; }
~M(){ cout << "~M"; }
};
C
class C : public A, public B {
M m;
public:
C(){ cout << "C";
}
~C(){ cout << "~C";
}
};
int main(){
C c;
cout << endl;
}
ABMC
~C~M~B~A
višestruko pojavljivanje baze
• ista bazna klasa se pojavljuje više puta u hijerarhiji
• više instanci bazne klase je sadržano u instanci izvedene klase
class auto {
protected:
engine e;
};
class
class
class
class
auto1
auto2
auto3
auto4
:
:
:
:
public
public
public
public
auto
auto
auto
auto
class perfect_auto :
public auto1,
public auto2,
public auto4
{
};
/* has 3 engines ! */
{...};
{...};
{...};
{...};
//
//
//
//
ima
ima
ima
ima
servo
klimu
ABS
zr. jastuke
A
A
B
B
C
virtualno izvođenje (virtual inheritance)
• omogućava da se ista bazna klasa pojavi više puta u hijerarhiji, a da je u izvedenoj
klasi sadržana samo jednom
• konstruktor virtualne baze se mora pozvati u najizvedenijoj klasi
class person {
protected:
string name;
public:
person(const string& name) : name(name) {}
};
class student : virtual public person { ... };
class employee : virtual public person { ... };
class emp_student : public student, public employee {
public:
emp_student(const string& name) : person(name) {}
};
person
student
employee
emp_student
6. predavanje
• operatori
friend
• klasa može omogućiti nekoj funkciji pristup njenim private članovima
• friend se može napisati bilo gdje u tijelu klase
class Number {
int n;
public:
Number(int n) : n(n) {}
friend Number sum(const Number& a, const Number& b);
};
Number sum(const Number& a, const Number& b){
return Number(a.n + b.n);
}
int main(){
Number n1(3), n2(4);
Number n3 = sum(n1, n2);
}
friend
• friend se može pisati i za funkcije članove
class A {
int n;
friend void B::f(A&);
};
class B {
void f(A& a) { ++a.n; }
};
• pokrata za sve funkcije članove neke klase: friend class
class A {
int n;
friend class B;
};
class B {
void f(A& a) { ++a.n; }
void g(A& a) { --a.n; }
};
• friend nije tranzitivan i ne nasljeđuje se izvođenjem
class A { friend class B; };
class B { friend class C; };
class C { }; // C nije friend od A
class D : public B {}; // D nije friend od A
operatori
• funkcija čije je ime "operator@"
class Number {
int n;
public:
Number(int n) : n(n) {}
friend Number operator+(const Number& a, const Number& b);
};
Number operator+(const Number& a, const Number& b){
return Number(a.n + b.n);
}
int main(){
Number n1(3), n2(4);
Number n3 = n1 + n2;
}
operatori
• može se pisati i kao funkcija član
class Number {
int n;
public:
Number(int n) : n(n) {}
Number operator+(const Number& b);
};
Number Number::operator+(const Number& other){
return Number(n + other.n);
}
int main(){
Number n1(3), n2(4);
Number n3 = n1 + n2;
}
preopterećenje operatora (operator overloading)
• operatori koji se mogu preopteretiti
+
=
|=
||
<
<<
++
*
>
>>
--
/
+=
>>=
->*
%
-=
<<=
,
^
*=
==
->
&
/=
!=
[]
|
%=
<=
()
~
^=
>=
new
!
&=
&&
delete
• važna pravila:
1. ne mogu se definirati novi operatori
2. ne može se mijenjati prioritet operatora
3. ne mogu se mijenjati sintaksna pravila
• binarni operatori:
1. globalna funkcija sa 2 argumenta (barem jedan od argumenata mora biti userdefined tip!) ili
2. funkcija član sa jednim argumentom
• unarni operatori:
1. globalna funkcija sa 1 argumentom ili
2. funkcija član bez argumenata
• operatori koji se moraju pisati kao članovi: =
[]
()
->
primjeri preopterećenja operatora
class Byte {
char n;
public:
Byte(unsigned char n=0) : n(n) {}
Byte operator+() const { return Byte(n); }
Byte operator-() const { return Byte(-n); }
Byte& operator+=(const Byte& other) { n += other.n; return *this; }
friend Byte operator+(const Byte& b1, const Byte& b2);
friend Byte operator-(const Byte& b1, const Byte& b2);
};
Byte operator+(const Byte& b1, const Byte& b2){
return Byte(b1.n + b2.n);
}
Byte operator-(const Byte& b1, const Byte& b2){
return Byte(b1.n - b2.n);
}
inkrement i dekrement
• da bi se razlikovali prefiksna i postfiksna verzija uvodi se dodatni argument
class Number {
int n;
public:
Number(int n) : n(n) {}
Number operator++() { return Number(++n); }
Number operator++(int) { return Number(n++); }
friend Number operator--(Number&);
friend Number operator--(Number&, int);
};
Number operator--(Number& a) { return --a.n; }
Number operator--(Number& a, int) { return a.n--; }
operatori <<, >>
• za unos i ispis objekata klase
class point {
int x, y;
friend istream& operator>>(istream&, point&);
friend ostream& operator<< (ostream& os, const point& a);
};
istream& operator>>(istream& is, point& p){
is >> p.x >> p.y;
return is;
}
ostream& operator<< (ostream& os, const point& p){
os << '(' << p.x << ',' << p.y << ')';
return os;
}
int main(){
point p;
cin >> p;
cout << p;
}
operatori implicitne pretvorbe (conversion operators)
• funkcija čije je ime "operator tip", nema tip povratne vrijednosti
class Number {
int n;
public:
Number(int n=0) : n(n) {}
operator int() { return n; }
};
int main(){
Number a;
int b = 2 + a; // 2 + a.operator int()
}
operator=
• ako nije napisan, generira ga compiler (slično ako copy konstruktor)
class Number{
int n;
public:
Number(int n=0) : n(n) {}
Number(const Number& other);
Number& operator=(const Number& other);
};
int main(){
Number a, b;
Number c = a; // poziva copy konstruktor!
b = a; // poziva operator=
}
Number& Number::operator=(const Number& other) {
n = other.n;
return *this;
}
Number::Number(const Number& other) { *this = other; } // koristi op=
privremeni objekti (temporary objects)
• nastaju prvi svakom slanju objekata u funkciju i vraćanju objekata iz funkcije
class A {
int n;
public:
A(int n) : n(n) { cout << "A"; }
A(const A& other) { n = other.n; cout << "A"; }
~A() {cout << "~A"; }
};
A f(A a){
return a; // poziva copy konstruktor! (return type = objekt)
}
int main(){
A a(3);
f(a); // poziva copy konstruktor! (argument funkcije = objekt)
}
operator[ ]
• kao indeks (argument) može primiti bilo koji tip
• može služiti za stvaranje asocijativnih polja
int main(){
assoc a;
string s;
while(true) {
cin >> s;
if(s.length()<=1)
break;
a[s]++;
}
cout << a;
}
struct snpair {
string s;
int count;
snpair() : count(0) {}
};
operator[ ]
class assoc {
snpair arr[256];
int last;
public:
assoc() : last(0) {}
int& operator[](const string& s);
friend ostream& operator<<(ostream& os, const assoc&);
};
int& assoc::operator[](const string& s){
for(int i=0; i<last; ++i)
if(arr[i].s == s)
return arr[i].count;
arr[last].s = s;
return arr[last++].count;
}
ostream& operator<< (ostream& os, const assoc& a){
for(int i=0; i<256; ++i)
if(a.arr[i].count)
os << a.arr[i].s << " : " << a.arr[i].count << endl;
return os;
}
operator()
• ako funkcija ima statičke parametre, može se napisati kao klasa (functional object)
class translate {
int cx, cy;
public:
translate(int cx, int cy) : cx(cx), cy(cy) {}
point& operator()(point& p){ p.x += cx; p.y += cy; return p; }
};
// translate all points for vector (3,4)
int main(){
translate t(3, 4);
point p;
while(1){
cin >> p;
t(p);
cout << p;
}
}
7. predavanje
• template
template klase
class IntArray {
int* p;
public:
IntArray(int size) : p(new int[size]){ }
~IntArray() { delete[] p; }
int& operator[](int i) { return p[i]; }
};
class DoubleArray {
double* p;
public:
DoubleArray(int size) : p(new double[size]){ }
~DoubleArray() { delete[] p; }
double& operator[](int i) { return p[i]; }
};
int main(){
IntArray ar1(10);
DoubleArray ar2(20);
}
template klase
• klasa parametrizirana tipom (ili tipovima)
template<typename T>
class Array {
T* p;
public:
Array(int size) : p(new T[size]){ }
~Array() { delete[] p; }
T& operator[](int i) { return p[i]; }
};
int main(){
Array<double> ar1(10);
Array<int> ar2(10);
}
template klase - definicija članova
• non-inline definiciju funkcija članova prethodi template<typename T>
template<typename T>
class Array {
T* p;
public:
Array(int size);
~Array();
T& operator[](int i);
};
template<typename T> Array<T>::Array(int size) : p(new T[size]){ }
//template<typename T> Array<T>::Array<T>(int size) : p(new T[size]){ }
template<typename T> Array<T>::~Array() { delete[] p; }
template<typename T> T& Array<T>::operator[](int i) { return p[i]; }
template funkcije
• funkcija parametrizirana tipom (ili tipovima)
void swap(int& a, int& b){
int t = a;
a = b;
b = t;
}
void swap(double& a, double& b){
double t = a;
a = b;
b = t;
}
void swap(Number& a, Number& b){
Number t = a;
a = b;
b = t;
}
template<typename T>
void swap(T& a, T& b){
T t = a;
a = b;
b = t;
}
int main(){
int n1, n2;
swap(n1, n2);
swap<int>(n1,n2);
}
template funkcije članovi (template member functions)
• funkcija član može biti template u template klasi ili običnoj klasi
• ne može biti virtualna
class A {
public:
template<typename T>
T fun(T a) { return a*a; }
template<typename T> class A {
T m;
public:
A(T t) : m(t) {}
};
int main(){
A a;
int n = a.fun(5);
}
template<typename ARG>
void f(ARG a) { cout << m*a; }
};
int main(){
A<unsigned char> a(2);
a.f(5);
}
generičko programiranje
• mogući parametri template-a:
1. konstantni izraz
2. objekt ili funkcija
3. pointer na člana
• template<typename T> vrijedi do kraja klase/funkcije
• template može imati više parametara, zadnji mogu imati default vrijednosti i ne
moraju se navesti (kao kod default vrijednosti argumenata funkcija)
template<typename T, int SIZE=64> class array { ... };
int main(){
array<int> ar1;
array<unsigned long, 32> ar2;
}
• template parametri funkcija se ne moraju navesti, osim za return type
template<typename T> T fun(int a){ return a*a; }
int main(){ fun<double>(3); }
instanciranje (template instantiation)
• compiler obavlja male sintaksne provjere odmah, kod za template klasu/funkciju
nastaje tek pri prvom spominjaju - instanciranje
template <typename T>
class Array {
T* p;
public:
Array(int size);
~Array();
T& operator[](int i);
};
template class Array<bool>; // eksplicitno!
typedef Array<int> IntArray;
int main(){
Array<float*> ar1(24); // implicitno
IntArray ar2(32); // implicitno
}
primjer
• funkcija sort
template<typename T> void sort(vector<T>& v){
const size_t n = v.size();
for(int gap=n/2; 0<gap; gap/=2)
for(int i=gap; i<n; ++i)
for(int j=i-gap; 0<=j; j-=gap)
if(v[j+gap] < v[j])
swap(v[j], v[j+gap]);
}
• zahtjeva operator< na tipu T
primjer
• funkcija sort
template<typename T> bool less(T a, T b) {
return a < b;
}
template<typename T> void sort(vector<T>& v){
const size_t n = v.size();
for(int gap=n/2; 0<gap; gap/=2)
for(int i=gap; i<n; ++i)
for(int j=i-gap; 0<=j; j-=gap)
if(less(v[j+gap], v[j]))
swap(v[j], v[j+gap]);
}
// da bi sort stringova radio pravilno
template<> bool less(const char* a, const char* b) {
return strcmp(a, b) < 0;
}
specijalizacija (specialization)
• alternativna implementacija za pojedini tip
template<>
class Array<bool> {
char* p;
public:
Array(int size) : p(new char[(size-1)/8+1]){ }
~Array() { delete[] p; }
bool operator[](int i) { return p[i/8] & (1<<(i%8)); }
};
int main(){
Array<int> ar1(30); // koristi originalni template
Array<bool> ar2(64); // koristi specijalizirani template
}
parcijalna specijalizacija (partial specialization)
• specijalizacija, ali ne po svim parametrima
template <typename T, typename U, typename V> class A {};
template <typename T, typename V> class A<T, int, V> {};
template <typename V> class A<double, long, V> {};
template <typename U> class A<int, U, int*> {};
eksplicitna konverzija (cast) u C++
• static_cast - za uobičajene (C-ovske) pretvorbe i upcasting
int n=5; double d = static_cast<double>(n);
Derived pd; Base* pb = static_cast<Base*>(&pd);
• dynamic_cast - za downcast, vraća NULL ako ne uspije
• klasa mora imati virtualne funkcije, potreban RTTI (run-time type information)
Base b;
Derived d;
Base* pb = static_cast<Base*>(&d);
Derived* pd = dynamic_cast<Derived*>(pb);
Derived* pd2 = dynamic_cast<Derived*>(&b); // pd2 je NULL!
eksplicitna konverzija (cast) u C++
• const_cast - za skidanje i stavljanje konstantnosti
void f(const A& a){
A& ra = const_cast<A&>(a);
ra.change();
A* pa = const_cast<A*>(&a);
pa->change();
}
• reinterpret_cast - za kompletnu promjenu tipa, opasno
void f(long lp){
A* pa = reinterpret_cast<A*>(lp);
}
int main(){
A a;
long xxx = reinterpret_cast<long>(&a);
f(xxx);
f(1); // argh!!
}
8. predavanje
• iznimke
iznimka (exception)
• kako signalizirati pogrešku iz operatora[] ili konstruktora?
• funkcija baca (throw) iznimku
• funkcija pozivatelj hvata (catch) iznimku
class X {};
double square(double v) {
if(v < 0) throw X();
...
return s;
}
int main(){
double d; cin >> d;
try {
double sq = square(d);
cout << "square(" << d << ") = " << sq << endl;
}catch(X){
cout << "invalid input" << endl;
}
}
iznimka je objekt
• objekt koji se baca može nositi poruku o greški
class X {
public:
string err;
X(const char* s) : err(s) {}
};
double square(double v) {
if(v < 0) throw X("argument manji od nule");
...
}
int main(){
double d; cin >> d;
try {
double sq = square(d);
cout << "square(" << d << ") = " << sq << endl;
}catch(X x){
cout << "error: " << x.err << endl;
}
}
višestruki catch blokovi
• catch blokovi se provjeravaju redom koji su napisani
class XNeg {};
class XSmall {};
double square(double v) {
if(v < 0) throw XNeg();
if(v < 0.01) throw XSmall();
...
}
int main(){
double d; cin >> d;
try {
double sq = square(d);
cout << "square(" << d << ") = " << sq << endl;
}catch(XNeg x){
cout << "negativan broj" << endl;
}catch(XSmall x){
cout << "premali broj" << endl;
}
}
hijerarhija iznimki
• obično se koristi hijerarhija klasa iznimki
class MathErr {
public:
virtual void ShowError() = 0;
};
class ZeroDivide : public MathErr {
public:
void ShowError() { cout << "dijeljenje s nulom"; }
};
class Overflow : public MathErr {
public:
void ShowError() { cout << "overflow"; }
};
class Underflow : public MathErr {
public:
void ShowError() { cout << "underflow"; }
};
catch by reference
• catch bazne klase, obavezno reference da bi radile virtualne funkcije
int main(){
double d; cin >> d;
try {
double sq = square(d);
cout << "square(" << d << ") = " << sq << endl;
}catch(MathErr& x){
x.ShowError();
}catch(...){
cout << "nepoznata iznimka" << endl;
}
}
• catch(...) hvata sve ostale iznimke
• u slučaju da nitko ne uhvati iznimku zove se std::uncaught_exception()
• pri bacanju iznimke zovu se destruktori svih objekata čija je inicijalizacija završena
• ne smije se throw-ati iz destruktora
deklaracija funkcija
• za svaku funkciju može se specificirati koje iznimke baca
double f(double v) throw (X1, X2);
// baca samo X1, X2 i izvedene
void f(int n) throw();
// ne baca nista!
int g();
// moze baciti bilo sto!
class IntArray {
public:
...
int& operator[](int index) throw (RangeError);
};
• ako funkcija baci neku drugu iznimku, zove se std::unexpected()
• ako virtualna funkcija specificira set iznimki, može se pregaziti sa funkcijom koja
baca isti set ili podskup
9. predavanje
• standardna biblioteka
standardna biblioteka (standard library)
• sve je unutar std namespace-a, u setu header-a
• #include <header> je obavezno zbog mogućih optimizacija compilera
• uglavnom template-i, dozvoljeno je izvođenje i specijalizacija
•
podjela prema tipu (u zagradi su headeri):
–
–
–
–
–
–
–
–
–
–
containeri (vector, list, deque, queue, stack, map, set, bitset)
opći servisi (utility, functional, memory, ctime)
iteratori (iterator)
algoritmi (algorithm, ...)
dijagnostika (exception, stdexcept, cassert, ...)
stringovi (string, cctype, cwtpe, ...)
ulaz/izlaz (iosfwd, iostream, ios, streambuf, istream, ostream, iomanip, fstream, ...)
lokalizacija (locale, ...)
podrška jeziku (limits, new, typeinfo,...)
numerika (complex, valarray, numeric, ...)
• Standard Template Library (STL) - containeri, algoritmi
standardni containeri
•
osobine:
–
–
–
–
–
•
vrste:
–
–
–
•
sekvencijalni - vector, deque, list
asocijativni (pristup po ključu) - map, set, multimap, multiset
posebni
• adapteri (sequence adapters) - stack, queue, priority_queue
• nepravi containeri - bitset, valarray, string
sadržavaju (preko typedef-a):
–
–
–
•
type safe
homogeni (svi elementi istog tipa)
neintruzivni (elementi ne moraju biti izvedeni iz bazne klase)
alokator kao dodatni template argument (za rad s memorijom)
imaju "jednak" interface (standardni nazivi i semantika)
value_type, allocator_type, size_type, difference_type
iterator, const_iterator, reverse_iterator,
const_reverse_iterator
pointer, const_pointer, reference, const_reference
osnovne funkcije za početak i kraj sekvence:
– begin(), end(), rbegin(), rend() - vraćaju iteratore
iterator
• za pristup standardnim containerima koriste se iteratori
• omogućavaju pristup različitim containerima na isti način
template <typename C>
C::value_type sum(const C& c){
C::value_type s = 0;
for(C::const_iterator it = c.begin(); it != c.end(); ++it)
s += *it;
return s;
}
čitanje
pisanje
pristup
iteriranje
usporedba
izlazni
ulazni
Out
In
=*p
*p=
++
->
++
==, !=
prema
naprijed
For
=*p
*p=
->
++
==, !=
dvosmjerni
Bi
=*p
*p=
->
++, -==, !=
za slučajni
pristup
Ran
=*p
*p=
->, []
++, --, +, -, +=, -=
==, !=, <, >, <=, >=
vector
•
funkcije za pristup elementima (vraćaju referencu):
– operator[](size_type index), at(size_type index)
– front(), back()
•
at() provjerava indeks i baca out_of_range iznimku
•
podržava funkcionalnost stack-a:
– push_back(const value_type& elem), pop_back()
int main(){
vector<int> v;
v.push_back(2);
v.push_back(7);
typedef vector<int>::const_iterator Iter;
for(Iter it = v.begin(); it != v.end(); ++it)
cout << *it << endl;
// isto kao:
for(int i=0; i<v.size(); ++i)
cout << v[i] << endl;
}
vector
•
za dodavanje u sredinu (vraća iterator):
– insert(iterator pos, const value_type& elem)
•
za brisanje iz sredine (vraća iterator):
– erase(iterator pos)
•
skupe operacije (zahtjevaju kopiranje preostalih elemenata)
•
za brisanje svih elemenata:
– clear()
•
funkcije vezane za veličinu:
– size_type size() - broj elemenata u vectoru
– bool empty()
– void resize(size_type n, value_type val = value_type()) dodane elemente inicijalizira sa val
– void reserve(size_type n) - alocira memoriju za n elemenata
– size_type capacity() - za koliko elemenata je alocirana memorija
ostali sekvencijalni containeri
• deque (double-ended queue)
•
kao vector, dodatno podržava :
– push_front(const value_type& elem), pop_front()
• list
•
ima front operacije, nema direktnog pristupa elementu (operator[ ])
•
koristi dvosmjerne iteratore pa je obično dvostruko povezana lista
•
dodatna funkcionalnost:
– splice(iterator pos, list& x) - prebaci sve element iz x ispred pos
– sort() - sortiraj listu (koristi operator<)
– merge(list& x) - spoji listu x (x ostaje prazan)
adapteri
• koriste sekvencijalne containere, ali imaju svoj interface
• ne koriste iteratore
• stack
•
po defaultu koristi deque (kao protected member), ali ima svoje funkcije :
– push(), pop(), top()
• queue
•
po defaultu koristi deque (kao protected member), ali ima svoje funkcije :
– push(), pop(), front(), back()
• priority_queue
•
slično kao queue (po defaultu koristi vector), ali elementi imaju prioritet kojim
dolaze na top() - po defaultu operator<
map
• drži parove ključ-vrijednost (value_type je std::pair<>), ključ mora biti jedinstven
• operator[](const key_type& key) - dodaje novi element ako ne nađe
postojeći
• sortira elemente (po defaultu operator<) da bi iteriranje išlo redom
int main(){
map<string, int> m;
while(true) {
string s; cin >> s;
if(s.length()<=1) break;
m[s]++;
}
typedef map<string, int>::const_iterator Iter;
for(Iter it = m.begin(); it != m.end(); ++it)
cout << it->first << " : " << it->second << endl;
}
• dodavanje preko insert():
string name("ivan"); int mark=5;
m.insert(make_pair(name, mark));
ostali asocijativni containeri
• multimap
•
dozvoljava duplicirane ključeve
•
zbog toga, dodatno podržava:
– lower_bound(), upper_bound(), equal_range()
• set
•
drži samo ključeve, moraju biti jedinstveni
• multiset
•
dozvoljava duplicirane ključeve
10. predavanje
• standardna biblioteka - algoritmi
copy
• kopiranje preko iteratora
vector<int> v(10);
deque<double> d(v.size());
copy(v.begin(), v.end(), d.begin());
• definicija funkcije
template<class InIt, class OutIt> inline
OutIt copy(InIt p, InIt e, OutIt x){
for (; p != e; ++p, ++x)
*x = *p;
return x;
}
copy - posebne primjene
• standarni iterator za ispis - ostream_iterator
ostream_iterator<int> os(cout, "\n"); // \n - separator
vector<int> v(10);
copy(v.begin(), v.end(), os);
• standarni iterator za unos - istream_iterator
istream_iterator<int> in(cin), ends;
vector<int> v;
copy(in, ends, back_inserter(v));
• ends - iterator sa default konstruktorom služi kao oznaka kraja
• back_inserter() - funkcija koja vraća back_insert_iterator
• back_insert_iterator - iterator koji radi push_back() u operator=()
fill, generate - punjenje containera
• punjenje konstantom
typedef vector<int>::iterator Iter;
vector<int> v(10);
for(Iter it = v.begin(); it != v.end(); ++it)
*it = -1;
// isto kao:
fill(v.begin(), v.end(), -1);
• punjenje funkcijom
typedef vector<int>::iterator Iter;
vector<int> v(10);
for(Iter it = v.begin(); it != v.end(); ++it)
*it = rand();
// isto kao:
generate(v.begin(), v.end(), rand);
funkcijski objekt
• klasa koja ima operator()
• za generate() - operator() nema argumenata, vraća vrijednost
class incr {
int n;
public:
incr(int initval=0) : n(initval) {}
int operator()() { return n++; }
};
int main(){
vector<int> v(10);
generate(v.begin(), v.end(), incr());
}
for_each, transform
• operator() prima element
class avg {
int n; double sum;
public:
avg() : n(0), sum(0) {}
void operator()(int elem) { ++n; sum += elem; }
operator double() { return sum/n; }
};
int main(){
istream_iterator<int> in(cin), ends;
vector<int> v;
copy(in, ends, back_inserter(v));
cout << "prosjek: " << for_each(v.begin(), v.end(), avg());
}
• vraćaju funkciju (funkcijski objekt)
• za transform() - operator() vraća novu vrijednost
count, find, replace, remove
• broj elemenata jednak nekoj vrijednosti
cout << "broj nula je: " << count(v.begin(), v.end(), 0);
• prvi element koji je jednak nekoj vrijednosti
Iter it = find(v.begin(), v.end(), 42);
if(it != v.end()) ... // nasao
• u stringu zamijeni sva slova 'a' sa 'e'
string s; cin >> s;
replace(s.begin(), s.end(), 'a', 'e');
• izbaci sva slova 'a'
string s; cin >> s;
s.erase(remove(s.begin(), s.end(), 'a'), s.end());
• remove() ne briše nego vraća iterator - obavezan poziv erase()
predikat - count_if, find_if, ...
• predikat - operator() prima element, vraća bool
• primjer: broj elemenata u vektoru manjih od 100
class less {
int limit;
public:
less(int limit) : limit(limit) {}
bool operator()(int n) { return n < limit; }
};
...
int cnt = count_if(v.begin(), v.end(), less(100));
• koristeći standardni objekt
count_if(v.begin(), v.end(), bind2nd(less<int>(), 100));
sort
• koristi operator< ili binarni predikat
• binarni predikat - operator() prima dva elementa, vraća bool
struct student {
string ime, prezime;
int ocjena;
};
struct comp {
bool operator() (const student& s1, const student& s2){
return s1.ocjena < s2.ocjena;
}
};
int main(){
vector<student> v;
// ... napuni container
sort(v.begin(), v.end(), comp());
}
podjela algoritama (potpuni spisak)
•
•
•
•
•
•
•
•
nemodificirajući na sekvenci: for_each, find, find_if,
find_first_of, adjacent_find, count, count_if, mismatch,
equal, search, find_end, search_n
modificirajući na sekvenci: transform, copy, copy_backward, swap,
iter_swap, swap_ranges, replace, replace_if, replace_copy,
replace_copy_if, fill, fill_n, generate, generate_n, remove,
remove_if, remove_copy_if, unique, unique_copy, reverse,
reverse_copy, rotate, rotate_copy
sortirane sekvence: sort, stable_sort, partial_sort,
partial_sort_copy, nth_element, lower_bound, upper_bound,
equal_range, binary_search, merge, inplace_merge, partition,
stable_partition
setovi: includes, set_union, set_intersection, set_difference,
set_symmetric_difference
operacije na heap-u: make_heap, push_heap, pop_heap, sort_heap
min/max: min, max, min_element, max_element,
lexicographical_compare
permutacije: next_permutation, prev_permutation
numerički: acccumulate, inner_product, partial_sum,
adjacent_difference