Osztály: a beépített adattípusokhoz hasonlóan kezelhető, felhasználói adattípus megvalósítása
Adattag: az osztályhoz tartozó adatok felépítését leíró deklarációk (a függvények nem)
struct S {
int i;
double d;
};
S s;
int i = s.i;
->
S* sp;
int i = (*sp).m;
int j = sp->m;
struct A {
int i;
};
struct B {
A a;
A* ap;
};
B b;
int i = b.a.i;
B* bp;
int j = bp->ap->i;
struct Link
{
Link* succ;
Link* prev;
int val;
};
struct List;
struct Link
{
int elem;
Link* succ;
List* main_list;
};
struct List
{
Link* head;
};
Statikus (osztályszintű) adattag: mindig pontosan egy példány van belőle, és ez magához az osztályhoz tartozik
struct S {
static int s;
int d;
};
Date::x
d.x // ha d Date típusú
int Date::x;
Tagfüggvény: az osztálydefiníción belüli függvénydeklaráció
struct Date {
void f(); // csak deklaráció
int g() { return 4; }; // definíció
...
};
void Date::f() { ... }
// f definíciója
struct szam {
string szoveg() {
return (poz ? "" : "-") + abszolut();
}
string abszolut() { ... };
...
};
Statikus tagfüggvény
struct peldanyszamlalo {
peldanyszamlalo() { ++p; }
~peldanyszamlalo() { --p; }
static int peldanyszam() { return p; };
static int p;
...
};
Konstans tagfüggvény
struct peldanyszamlalo {
peldanyszamlalo() { ++p; }
~peldanyszamlalo() { --p; }
int peldanyszam() const { return p; };
static int p;
...
};
this mutató
T*
, konstans tagfüggvényben const T*
struct s {
int a;
s(int a) {
this->a = a;
}
};
Módosító függvények
Date d;
d.add_day(3).add_month(2).add_year(4);
((d.add_day(3)).add_month(2)).add_year(4);
T&
visszatéréssel rendelkezzenekDate& Date::add_year(int n) {
// módosító kódrészlet
return *this;
}
Operátorok
::
hatókör-feloldás.
tagkiválasztás.*
kiválasztás tagmutatóval?:
feltételes kifejezéssizeof
typeid
Operátorok
struct X {
X operator+(X);
double operator+(int);
};
X operator+(int, X);
X a, b;
X c = 2 + a + b + 1;
struct X {
X operator++(); // egyoperandusú előtag, ++x
X operator++(int); // egyoperandusú utótag, x++
};
X operator++(X); // egyoperandusú előtag, ++x
X operator++(X, int); // egyoperandusú utótag, x++
=
, []
, ()
és ->
operátorok
operator[]
: indexelésoperator->
: indirekció (hivatkozás feloldása)operator->
: mutatóval kell visszatérnieoperator()
operátor
operator()
operátor
struct X {
int operator()();
int operator()(int);
int operator()(char);
};
X a;
int i = a() + a(1) + a('a');
=
, &
, vesszőprivate
részbe, hasonlóan a kostruktorhoz és az értékadó operátorhoz==
, !=
<
, >
, <=
, >=
X+int
operátorhoz sem jön létre int+X
Interfész: az osztály által a külvilág számára felkínált műveletek összessége
struct s {
public: // nyilvános szakasz kezdete
int nyilvanos();
private: // átlátszatlan szakasz kezdete
int rejtett;
};
Invariáns: az objektum élettartama alatt végig fennálló, az objektumra vonatkozó tulajdonság
struct
és class
kulcsszavak
struct
public
szakasszal kezdődikclass
private
szakasszal kezdődikstruct
és class
kulcsszavak
struct
utalhat arra, hogy csak egy adatszerkezetről van szóAdattagok elrejtése (information hiding): egyetlen adattag sem nyilvános
Értékadás
struct A {
int i;
double d;
};
A a = { 15, 642.9 };
Konstruktor (constuctor): kezdetiérték-adó függvény
struct racionalis {
racionalis(int, int);
private:
int szamlalo;
int nevezo;
};
Alapértelmezett konstruktor: paraméter nélküli konstruktor
Date d = Date(1,2,3);
Date d(1,2,3);
// ugyanannak a konstruktornak kétféle hívása
Date d;
// alapértelmezett konstruktor hívása
// ha nincsen, fordítási idejű hiba
Tag-kezdőérték lista (member initializer)
struct s {
Date(int a, int b, int c)
: aa(a), bb(b), cc(c, 123.0) { ... }
int aa;
int bb;
sajat_osztaly cc;
// van sajat_osztaly(int, double) konstruktora
}
Másoló konstruktor (copy constructor)
Másoló konstruktor
private
elérésűÁltalános séma másoló konstruktorra és értékadás operátorra
struct T {
// másoló kostruktor
T(const T& t);
// értékadó operátor
T& operator=(const T& t);
// destruktor
~T();
private:
// segédfüggvények
T copy(const T& t);
void T::clear();
}
void T::clear() {
// kitöltendő:
// felszabadítja az
// osztályban lefoglalt erőforrásokat
// (pl. memóriaterületeket)
}
T T::copy(const T& t) {
// kitöltendő:
// az objektum helyes másolásának egyedi kódja
}
T::T(const T& t) {
copy(t);
}
T& T::operator=(const T& t) {
if (&t != this) { // önmagunkat nem módosítjuk
clear();
copy(t);
}
}
// a destruktor gyakran így néz ki
T::~T() {
clear();
}
struct complex {
complex(int x = 0, int y = 0) {...}
};
complex z = 2;
// ekvivalens ezzel:
// complex z = complex(2, 0);
struct Pont { Pont(int x, int y = 0) {...} };
void f(Pont) { ... }
f(2);
// ekvivalens ezzel:
// f(Pont(2, 0));
explicit
minősítővel ellátott konstruktor: nem hívódik meg ott, ahol másoló konstruktorra van szükségDestruktor
struct s {
s() { mem = new int[10000]; }
~s() { delete[] mem; }
int* mem;
};
Erőforrások robosztus kezelése
Eroforras e;
try {
e = lefoglal();
kezel(); // kivétel váltódik ki
// 1. lehetőség
} catch (...) {
// 2. lehetőség
}
// 3. lehetőség
Eroforras e;
try {
e = lefoglal();
kezel(); // kivétel váltódik ki
// 1. lehetőség
} catch (...) {
// 2. lehetőség
}
// 3. lehetőség
Erőforrás ≡ Osztály
auto_ptr
<memory>
fejléc tartalmazzaauto_ptr
-t nem lehet másolniauto_ptr
mutathatfinally
ág a catch
-ek utánÖröklődés, származtatás
Liskov-féle helyettesítési elv (1987): egy altípusra mindig álljon fenn az őstípus minden tulajdonsága
struct EPolg { ... };
struct Hallgato { EPolg e; ... };
struct Hallgato : public EPolg { ... }
Hallgato* h;
EPolg* ep = h;
struct EPolg { void kiir() const {...} };
struct Hallgato : public EPolg {
void kiir() const {
kiir(); // a Hallgato művelete
EPolg::kiir(); // az EPolg művelete
}
};
struct EPolg {
EPolg(string n) : nev(n) { ... }
string nev;
};
struct Hallgato : public EPolg {
Hallgato(int e, string n)
: evfolyam(e), EPolg(n) { ... }
int evfolyam;
};
struct EPolg {
EPolg(const EPolg&);
EPolg& operator=(const EPolg&);
};
struct Hallgato : public EPolg {
Hallgato(int e, string n)
: evfolyam(e), EPolg(n) { ... }
int evfolyam;
};
Hallgato h;
EPolg e = h; // csak az EPolg rész konstruálódik meg
e = h; // csak az EPolg rész másolódik le
Osztályhierarchia
Virtuális függvény (metódus)
struct EPolg {
string nev;
virtual void orara_megy() const {
cout << nev << " polgar orara megy" << endl;
};
};
struct Hallgato : public EPolg {
void orara_megy() const {
cout << nev << " hallgato orara megy" << endl;
};
};
Hallgato h;
EPolg& e = h;
e.orara_megy();
// EPolg változóra hívtuk meg,
// de a Hallgato függvénye hajtódik végre
g++
: -fdump-class-hierarchy
kapcsolóTisztán virtuális függvény: nincsen definíciója az adott osztályban
Absztrakt osztály: tartalmaz tisztán virtuális függvényt
struct Alakzat {
virtual void rajzol() const = 0;
};
Többszörös öröklődés: egy osztálynak több őse is lehet
struct A { virtual void f() { ... } };
struct B : public A { virtual void f() { ... } };
struct C : public A { virtual void f() { ... } };
struct D : public B, public C { virtual void f() { ... } };
struct E : public B, public C { };
D d; E e;
d.f(); // a D-beli f-et hívja
e.B::f(); // egyértelműsíteni kell
e.A::f(); // hiba: két A-t is öröklünk, nem egyértelmű
Többszörös öröklődés
A
öröklődjön egyszeresenstruct A { virtual void f() { ... } };
struct B : public A { virtual void f() { ... } };
struct C : public A { virtual void f() { ... } };
struct D : public B, public C { virtual void f() { ... } };
struct E : public B, public C { };
// E helytelen: felül kell bírálni benne f-et
D d;
e.B::f();
e.A::f(); // most már ez is egyértelmű
Barát függvény: olyan osztályon kívüli függvény, amely hozzáférhet az osztály átlátszatlan részéhez is
friend
minősítővelstruct S {
friend istream& operator>>(istream&, S&);
};
istream& operator>>(istream& is, S& s) { ... }
Barát függvény
friend class
Beágyazott osztály (embedded/nested class) vagy tagosztály (member class)
Beágyazott osztály
struct S {
struct T {
T(int);
double f(double, string);
};
};
// a beágyazott osztály függvényeinek definíciói
S::T::T(int i) { ... }
double S::T::f(double d, string s) { ... }
Generatív programozás: új kódrészlet programmal történő előállítása
Generikus programozás: algoritmusok és adatszerkezetek típusfüggetlen megvalósítása
C++ generikus nyelvi eszköz: sablon (template)
typename Név
class Név
template <class T, int msize> class Buffer {
T v[];
const int max_size;
public:
Buffer() : max_size(msize) {}
...
};
template <class T, T t> class C { ... };
template <class T>
struct fa {
void bejaras() const;
...
};
template <class T>
void fa<T>::bejaras() const { ... }
Példányosítás (instantiation): konkrét osztály vagy függvény készítése a sablonból
Példányosítás
template <class T>
void f(T t) { cout << t.x; }
f<int>(1); // hiba: int-nek nincsen x adattagja
struct x { int x; };
f<x>(x()); // x-ben van x adattag
vector<list<int>> v; // helytelen
vector<list<int> > v; // helyes
>>
operátortemplate <int i> class sablon_osztaly { ... };
sablon_osztaly<256 >> 3> s;
Függvénysablonok példányosítása
#include <algorithm>
int x = 4, y = 2;
double u = 1.2, v = 2.1;
swap(x,y); // swap(int, int)
swap(u,v); // swap(double, double)
swap<int>(x,y);
template <class T> T max(T, T);
max<int>('a', 1);
max<double>(2.7, 6);
// minősítés nélkül mindkettő többértelmű lenne,
// mert konverzióra lenne szükség
template<class T = int, int meret = 100>
struct verem { ... };
Stack<> s1;
Stack<int, 100> s2; // típusa azonos s1-gyel
Stack<double, 20> s3;
template<class T>
T sum(T* b, T* e, T init = T()) {
while (b != e) init += *b++;
return init;
}
Specializáció (specialization): a típusparaméterek bizonyos kombinációja esetén az alapesettől eltérő definíció használata
Specializáció
template<class A, class B, class C, class D>
struct S { ... };
template<>
struct S<int, int, int, int> { ... };
// parciális specializáció
template<class B, class C, class D>
struct S<int, B, C, D> { ... };
// parciális specializáció
template<class A, class B, class C, class D>
struct S<A*, B, C, D*> { ... };
Sablonok hibaüzenetei
Metaprogramozás: program készítése vagy átalakítása program segítségével
Metaprogramozás
#include <iostream>
template <int N>
struct Factorial {
enum { value = N * Factorial<N-1>::value };
};
// specializáció: a rekurzív példányosítás vége
template <>
struct Factorial<1> { enum { value = 1 }; };
const int fact15 = Factorial<15>::value;
Többalakúság vagy polimorfizmus (polymorphism): különböző adattípusok közös kezelése
Univerzális polimorfizmus: egyetlen absztrakt megvalósítás tetszőleges (megfelelő tulajdonságú) típusra működik
Ad hoc polimorfizmus: csak típusok véges halmazán működik
Explicit típuskonverziók (cast)
Explicit típuskonverziók (cast)
static_cast
: egymással kapcsolatban levő típusok konverziójastruct S : public P { ... };
P p;
S s = static_cast<S>(p);
int i = static_cast<int>(4.0);
Explicit típuskonverziók (cast)
reinterpret_cast
: egymással kapcsolatban nem levő típusok konverziójaint* pi;
char* pc = reinterpret_cast<char*>(pi);
Explicit típuskonverziók (cast)
const_cast
: a konstans minősítő eltávolításaconst int i = 1;
const int* pi = &i;
int* p2i = const_cast<int*>(pi);
*p2i = 2;
// i értéke 2 lett
"C stílusú" típuskonverzió
static_cast
reinterpret_cast
dynamic_cast
int i = (int)4.0;
int i = int(4.0);
// mindkét változat ekvivalens
Explicit típuskonverziók (cast)
dynamic_cast
: ősre vonatkozó mutató vagy referencia átalakítása leszármazottbelirebad_cast
kivétel váltódik ki// dynamic_cast példa
struct A {
void f() { cout << "A"; }
virtual void g() {}
};
struct B : public A {
void f() { cout << "B"; }
};
struct C : public A {
void f() { cout << "C"; }
};
// dynamic_cast példa (folyt.)
void f(A* arg) {
B* bp = dynamic_cast<B*>(arg);
C* cp = dynamic_cast<C*>(arg);
bp ? bp->f() : cp ? cp->f() : arg->f();
}
int main() {
A a; B b; C c;
A *ap = &a, *bp = &b, *cp = &c;
f(ap); f(bp); f(cp);
}
dynamic_cast
példa kimenete: ABC
f
-ben static_cast
lenne, az nem használná ki a futási idejű információkat, és BBB
lenne az eredményA*
és C*
is átalakul B*
-gáf
virtuális lenne A-ban, akkor mégis megtalálná a megfelelőt: ABC