OO paradigmák, osztály, operátorok átdefiniálása
Szeberényi Imre, Somogyi Péter BME IIT szebi@iit.bme.hu
// Kiírunk egy stringet void print(String str)
00401350 push ebp
00401351 mov ebp,esp
00401353 sub esp,40h
00401356 push ebx
00401357 push esi
00401358 push edi
00401359 lea edi,[ebp-40h]
0040135C mov ecx,10h
00401361 mov eax,0CCCCCCCCh
00401366 rep stos dword ptr [edi]
00401368 mov eax,dword ptr [ebp+8]
0040136B push eax
0040136C push offset string "%s" (0042201c)
00401371 call printf (004037d0)
00401376 add esp,8
00401379 pop edi
0040137A pop esi
0040137B pop ebx
0040137C add esp,40h
0040137F cmp ebp,esp
00401381 call __chkesp (00403620)
00401386 mov esp,ebp
00401388 pop ebp
00401389 ret
00401350 55 8B EC 83 EC 40 53 56 U‹ě.ě@SV
00401358 57 8D 7D C0 B9 10 00 00 WŤ}Ŕą...
00401360 00 B8 CC CC CC CC F3 AB .¸ĚĚĚĚó«
00401368 8B 45 08 50 68 1C 20 42 ‹E.Ph. B
00401370 00 E8 5A 24 00 00 83 C4 .čZ$...Ä
00401378 08 5F 5E 5B 83 C4 40 3B ._^[.Ä@;
00401380 EC E8 9A 22 00 00 8B E5 ěčš"..‹ĺ
00401388 5D C3 CC CC CC CC CC CC ]ĂĚĚĚĚĚĚ
fokozatos finomítás
PASCAL nyelv (blokkok fa struktúrája)
modulok között a kötés gyenge
FORTRAN, MODULA-2, ADA
Olvassunk be 10 komplex számot és írjuk ki a számokat és abszolút értéküket fordított sorrendben! - Funkcionális dekompozíciónál az adatokon végzett tevékenységekre koncentrálunk:
Tevékenység | Adat |
---|---|
beolvasas() és tárolás |
Komplex , KomplexTomb |
kiiras() |
Komplex , KomplexTomb |
abs() |
Komplex |
struct Komplex {
double re, im;
};
int main() {
Komplex t[10]; // adatok
beolvasas(t); // művelet
kiiras(t); // művelet
return 0;
}
#include <cmath>
#include <iostream>
using namespace std;
double abs(Komplex k) { // adatot ismerni kell
return sqrt(k.re * k.re + k.im * k.im);
}
void beolvasas(Komplex t[]) { // ismerni kell
for (int i = 0; i < 10; i++) {
cin >> t[i].re >> t[i].im;
}
}
void kiiras(Komplex t[]) { // ismerni kell
for (int i = 9; i >= 0; i--) {
cout << t[i].re << '+' << t[i].im << 'j' << abs(t[i]) << endl;
}
}
Olvassunk be 10 komplex számot és írjuk ki a számokat és abszolút értéküket fordított sorrendben!
Objektum orientált dekompozíció használatakor az absztrakt adatra koncentrálunk:
Szereplő (obj.) | Művelet (üzenet) |
---|---|
Komplex |
beolvas() , kiir() , abs() |
KomplexTar |
tarol() , elovesz() |
Komplex k; // beolvas, kiir, abs
Komplex t; // tarol, elovesz
for (int i = 0; i < 10; i++) {
k.beolvas(); // a k objektum beolvas
t.tarol(i, k); // és a t obj. tárol
} // műveletét aktivizáljuk
for (int i = 9; i >= 0; i--) {
k = t.elovesz(i);
k.kiir();
cout << ' ' << k.abs() << endl;
}
struct Komplex {
double re, im;
};
// összetartozásra csak a név utal
void beolvasKomplex(Komplex *kp);
double absKomplex(Komplex *kp);
void setKomplex(Komplex *kp, double r, double i);
int main() {
double f;
struct Komplex k1, k2; // deklaráció és definíció
setKomplex(&k1, 1.2, 0); // inicializálás
f = absKomplex(&k1); // Névtér hiánya
f = absKomplex(&k2);
}
setKomplex(Komplex *kp, double r, double i);
// ^ funckió + obj. típusa
// *kp: melyik konkrét adat
// r, i: művelet operandusai
void beolvasKomplex(Komplex *kp);
double absKomplex(Komplex *kp);
Ilyen paraméterezést használtunk a gyakorlaton a String
esetében is.
struct Komplex {
double re, im; // adatok
void set(double r, double i); // tagfüggvények
double abs();
};
int main() {
Komplex k1, k2;
// A fv. névben elég a funkciót jelölni.
// A saját adatot sem kell átadni.
k1.re = 1.2;
k1.im = 0;
k1.set(1.2, 0);
double f = k1.abs();
set(&k1, 1.2, 0); // vs. setKomplex(&k1, 1.2, 0);
}
struct Komplex {
private: // privát adatok
double re, im;
public: // nyilvános tagfüggvények
void set(double r, double i);
double abs();
};
int main() {
Komplex k1;
// Közvetlen hozzáférés a priváthoz TILOS:
// k1.re = 1.2; k1.im = 0;
// helyette csak így:
k1.set(1.2, 0);
double f = k1.abs();
}
Objektum ≡ Egy viselkedési osztály egy konkrét példánya.
C++-ban a struct
egy osztály!
C++
class Komplex {
double re, im;
public:
void set(double r, double i) {
re = r;
im = i;
}
};
int main() {
Komplex k1;
k1.set(1.2, 3.4);
}
C:
struct Komplex {
double re, im;
};
void setKomplex(struct Komplex *this, double r, double i) {
// a konkrét objektumra mutat ^
this->re = r;
this->im = i;
}
int main() {
struct Komplex k1;
setKomplex(&k1, 1.2, 3.4);
}
class Komplex {
double re, im;
public:
void set(double re, double im) {
this->re = re; this->im = im;
} // ^ ^ k2.set(2.1, -4);
double abs();
};
#include <cmath>
double Komplex::abs() {
return sqrt(this->re * this->re + this->im * this->im);
// *this azt az objektumot jelenti, amelyre a tagfüggvényt
} // meghívták. Itt a *this a k1 lesz:
int main() { Komplex k1, k2; k1.set(1, 2); double f = k1.abs();
}
class Komplex {
double re, im; // adatok privátak
public:
void set(double r, double i) { re = r; im = i; }
// ^ inline-nak megfelelő
double abs();
};
#include <cmath>
#include <iostream>
double Komplex::abs() { return sqrt(re * re + im * im); }
int main() { //^ scope operátor
Komplex k1; k1.set(1.2, 3.4);
std::cout << k1.abs();
}
class Komplex {
double re, im;
public:
void set(double r, double i) {
re = r;
im = i;
}
double getRe() const { return re; }
double getIm() const { return im; }
double abs() const;
}; // Nem változtat(hat)ja meg az állapotot (adatokat)
#include <cmath>
double Komplex::abs() const { return sqrt(re * re + im * im); }
Automatikusan keletkező (implicit deklarált):
KONSTRUKTOR: Az objektum létrejöttekor hívódik. Feladata, hogy alapállapotba hozza az objektumot. Ha nem deklarálunk egyet sem, akkor implicit jön létre.
class Komplex {
double re, im;
public:
Komplex() {} // konstruktornak nincs visszatérési típusa
// Ilyen üres programozott törzs keletkezik implicit módon
Komplex(double r, double i) { re = r; im = i; }
};
int main() {
Komplex k1; // paraméter nélkül hívható (default)
Komplex k2 = k1; // másoló (copy) ctr. (ez most implicit)
Komplex k3 = Komplex(1.2, 3.4);
// ^ ideiglenes objektum
}
DESTRUKTOR: Az objektum megszüntetésekor hívódik. Alapvető feladata, hogy megszüntesse az obj. által din. mem. területen létrehozott objektumokat/adatokat.
class Komplex {
double re, im;
public:
~Komplex() {} // destruktornak paramétere sincs
// ^ Ez keletkezne implicit módon
};
int main() {
Komplex k1; // paraméter nélkül hívható (default)
Komplex k2 = k1; // másoló (copy) konstruktor
} // destruktorok hívódnak
#include <iostream>
class Komplex {
double re, im;
public:
Komplex(double r) { re = r; }
Komplex(double r, double i) { re = r; im = i; }
double getRe() const { return re; }
double getIm() const { return im; }
~Komplex() { std::cout << "Nincs mit megszüntetni"; }
};
int main() {
Komplex k1(1.3, 0); // definíció és inic.
Komplex k2(3); // Komplex k3; // Nincs ilyen ctr.
} // destruktorok meghívódnak
#include <iostream>
class Komplex {
double re, im;
public:
Komplex(double r = 0, double i = 0) { re = r; im = i; }
double getRe() const { return re; }
double getIm() const { return im; }
void kiir(std::ostream &os = std::cout) const; // *
~Komplex() { std::cout << "Nincs mit megszüntetni"; }
};
void Komplex::kiir(std::ostream &os) const { // *
os << re << '+' << im << 'j';
} // * Csak az egyik helyen, tipikusan a deklarációnál
// jelöljük a default-ot!
Objektum létrehozása alapállapottal.
Automatikusan hívódik minden olyan esetben, amikor az objektumnak alapállapotban kell létrejönnie. Nem keletkezik implicit, ha van legalább egy explicit. Pl:
Komplex k1;
)tömbelemek létrehozásánál (Komplex kt[10];
)
tartalmazott objektumoknál
származtatásnál (ld. később)
#include <iostream>
class Komplex {
double re, im;
public:
Komplex(double r = 0, double i = 0) { re = r; im = i; }
double getRe() const { return re; } // getIm() idem.
void kiir(std::ostream &os = std::cout) const;
void setRe(double r) { re = r; } // setIm(double) idem.
void beolvas(std::istream &is = std::cin);
};
int main() {
Komplex k1, k2(1, 1), kt[10], k3; kt[2].kiir();
Komplex *kp = new Komplex[100]; // mi történik itt?
delete[] kp;
} // itt mit ír ki?
+ néhány alapértelmezett dolgot, amit az ajándékba kapott implicit deklarált tagfüggvények valósítanak meg pl:
Összeadni nem tud?
k2.operator+(k3)
formára illeszkedő tagfüggvénye ha nincs, vagy beépített típus és a jobb old. obj., akkor:operator+(k2, k3)
formára illeszkedő fv.=
-vel is, de ehhez van implicit deklarált fv. abban az esetben, ha mindkét oldal azonos típusú, aminek a hatása az, amit várunk: értékadás.class Komplex {
double re, im;
public:
Komplex(double r = 0.0, double i = 0.0) {
re = r; im = i; }
Komplex operator+(const Komplex &k) const {
Komplex sum(k.re + re, k.im + im); return sum;
}
Komplex operator+(double r) const {
return operator+(Komplex(r)); }
};
int main() { Komplex k1, k2, k3; k1 + k2; k1 + 3.14; k1 = k2; }
// 3.14 + k1; bal oldal nem obj.! Ezért globális fv. kell!
class Komplex {
double re, im;
public:
Komplex(double r, double i) {
re = r;
im = i;
}
};
// Globális fv., nem tagfüggvény:
Komplex operator+(double r, const Komplex &k) {
return Komplex(k.re + r, k.im);
}
// Baj van! Nem férünk hozzá, mivel privát!
class Komplex {
double re, im;
public:
Komplex(double r, double i = 0) {
re = r; im = i;
}
double getRe() const { return re; } // publikus lekérdező
double getIm() const { return im; } // függvények
};
// Globális fv., nem tagfüggvény:
Komplex operator+(const double r, const Komplex &k) {
return Komplex(k.getRe() + r, k.getIm());
}
A bal oldal objektum ugyan, de nincs a kezünkben. Ezért csak egy operator<<(cout, k1)
hívásra illeszthető globális függvénnyel lehet megoldani:
#include <iostream>
std::ostream &operator<<(std::ostream &os, const Komplex &k) {
k.kiir(os);
return os;
}
// std::ostream &-t ad vissza, így láncolható:
// cout << k1 << k2;
A bal oldal objektum ugyan, de nincs a kezünkben. Ezért csak egy operator>>(cin, k1)
hívásra illeszthető globális függvénnyel lehet megoldani:
#include <iostream>
std::istream &operator>>(std::istream &is, Komplex &k) {
k.beolvas(is);
return is;
}
// A kiir() és a beolvas() tagfüggvény akár el is hagyható:
std::ostream &operator<<(std::ostream &os, const Komplex &k) {
return os << k.getRe() << '+' << k.getIm() << 'j';
}
.
::
?:
sizeof
operator++()
– pre (++i
)operator++(int)
– post (i++
)operator double()
– cast (double)
operator[](typ i)
– index (typ
tetszőleges)operator()()
– függvényhívás(double)Komplex(3, 5)
– mit jelent?cout << 1;
Komplex k1, k2;
double d = (double)k1; // mit jelent? valós rész? abs?
// Jelentse a valós részt:
/* Komplex {
// …
operator double() { return re; }
// formálisan nincs típusa !!!
};
*/
Veszély! A típuskonverzió automatikus is lehet!
Pl: k1 + 3.14
→ (double)k1 + 3.14
lesz, ha nincs operator+(Komplex, double)