Az 1990-es évek óta az objektum-orientált programozási stílus előtérbe
került a strukturált programozással szemben. A strukturált program adatból és
algoritmusból épül fel, míg az objektum-orientált program emellett absztrakciót
is végez. Az absztrakció az objektumok révén valósul meg, melyek
leegyszerűsítik az adatok és algoritmusok kezelését. Az objektumokat
tulajdonságaik alapján osztályozni lehet, az adott osztályhoz tartozó
objektum pedig az osztály példányának tekinthető.
1. Osztályok és objektumok
2. Öröklés
3. Struktúrák
4. Osztályok
5. Kódrendezés
6. Konstruktorok és destruktorok
7. Objektumok másolása
8. Konstans objektumok és konstans tagfüggvények
9. Osztályok illesztése
10. Friend függvények és friend osztályok
11. A this mutató
12. Az osztályok statikus tagjai
13. Proxy osztályok
1. Osztályok és objektumok
Példaképp adott egy négyzethálós kép. Minden négyzet egy objektum,
melynek van egy pozíciója és egy színe. Ezzel a két adattal művelet hajtható
végre, például megváltoztatható a szín vagy újra rajzolható a négyzet. Mivel minden négyzetnek
van színe és pozíciója, ezért egy osztályba sorolható, a Négyzet osztályba.
A Négyzet osztály létrehozása egy adattípus létrehozásával ér fel, mert lehetővé teszi a Négyzet típusú változók deklarálását. Ezeket a változókat a Négyzet osztály objektumainak hívjuk. Például a
Négyzet obj utasítás az obj objektumot deklarálja, amely Négyzet típusú, és rendelkezik a szín és pozíció tagváltozókkal és a Színez, Rajzol tagfüggvényekkel.
2. Öröklés
Nem csak négyzetek, hanem háromszögek is részesei lehetnek a képnek.
Ezeknek ugyanazok a tulajdonságai vannak és
ugyanazok a műveletek végezhetőek el velük, de mégsem teljesen ugyanazok, mert például az újrarajzoló algoritmus
más. Továbbá beiktatható egy új tulajdonság ami méginkább megkülünbözteti a háromszöget a négyzettől: a csúcsok
száma.
A hasonlóságok miatt ezt a két osztályt egy felsőbb absztrakciós szinten közös osztályba lehet sorolni: a Formák osztályba.
A Formák
osztály még absztraktabb a Négyzet és
Háromszög osztályoknál, mert
méginkább kiiktatja a kevésbé fontos információkat. Nem foglalkozik azzal, hogy
a forma négyzet-e vagy háromszög, az majd lentebb eldől. Magába foglalja a
közös tulajdonságokat, így ezeket nem kell külön deklarálni mindenik alsó
osztályban. Az alsó osztályok (Négyzet és
Háromszög) megöröklik a Formák tulajdonságait. Ami nem egyforma
azt felülírja mindenik a maga osztályában (például a csúcs tagváltozót és a Rajzol tagfüggvényt). A Négyzet és a Háromszög
osztályok származtatott (vagy derivált) osztályai a Formák
osztálynak. A Formák osztály a
származtatott osztályok ősosztálya (vagy alaposztálya). Az öröklődés célja, hogy közös
tulajdonságok általánosabbá váljanak kezelhetőbbé téve az
információt.
3. Struktúrák
A hagyományos strukturált programozási nyelv a C. A Pascal, a Deplphi és
a C++ csupán támogatják az objektum-orientált módszereket, azonban alapjában
ezek is strukturált programozási nyelvek. Tisztán objektum-orientált például a
Java és a C#. A C++-ban
használt osztályok a C-ben használt struktúrákból fejlődtek ki. Összesített
adattípust jelentenek, azaz különböző típusú adatokra és függvényekre lehet
egyetlen változóval hivatkozni, amit objektumoknak hívunk.
Példa:
#include
using namespace std;
struct Time
{
int hour; //0-23
int minute; //0-59
int second; //0-59
void printShort();
void printLong();
};
void Time::printShort() //13:30
{
cout
"0" : "")
":"
"0" : "")
}
void Time::printLong() //1:30:10 PM
{
cout
":"
"0" : "")
":"
"0" : "")
"
"
"AM" : "PM");
}
int main()
{
Time
timeObject, // Time típusú objektum
*timePtr, // Time típusú mutató
&timeRef = timeObject;// Time típusú referencia
timeObject.hour
= 6;
cout
"timeObject.hour = "
cout
"timeRef.hour = "
timePtr
= &timeObject;
cout
"timePtr->hour = "
hour
Time
Lunch;
Lunch.hour
= 13;
Lunch.minute
= 30;
Lunch.second
= 10;
cout
"Ebéd idő: ";
Lunch.printShort();
cout
" azaz ";
Lunch.printLong();
return 0;
}
A fenti programban egy Time nevű struktúra van definiálva, mely 3
változóval és 2 függvénnyel rendelkezik. A függvényeket lehetne a struktúrán
belül is definiálni, azonban szebb ha külön, a struktúrán kívül definiáljuk.
Mivel több struktúrának is lehet azonos nevű függvénye, a függvény neve elé
dupla kettősponttal oda kell írni a struktúra nevét is ahová tartozik.
A program elején három darab Time típus
van deklarálva: egy objektum, egy mutató és egy referencia. A struktúra
definiálása önmagában nem foglal semmi memóriát, ha nem használjuk akkor
szabadon marad a neki szánt tárhely. Amikor azonban egy Time típusú objektumot
deklarálunk, akkor lefoglalódnak a struktúrában deklarált változók helyei. A
struktúra tagjai a pont (.) és a nyíl (->) operátorokkal érhetőek el. A
pontot az objektumon vagy az objektum referenciáján keresztül való elérésénél
használjuk, míg a nyilat akkor, ha mutató segítségével hivatkozunk az
objektumra. A program kimenete a következő:
timeObject.hour
= 6
timeRef.hour = 6
timePtr->hour
= 6
Ebéd idő: 13:30
azaz 1:30:10 PM
A struktúrák a lehetőségek szempontjából korlátozottabbak
az osztályoknál, például nem lehet privát változókat deklarálni, melyeket csak
a tagfüggvények érhetnének el, mert alapból minden publikus, azaz kívülről
hozzáférhető. A programírás felügyelete pedig sokkal korlátlanabb a
struktúráknál, például megengedi, hogy inicializálatlanul hagyjuk a változókat
vagy hogy ne ellenőrizzük le annak helyességét (pl. egy interfészen keresztül). Az
osztályok esetén az ilyen hibákra már a fordításnál fény derül és ezzel
csökken a hibás programok írásának esélye. A gyakorlott programozók a
struktúrákkal is meg tudják oldani mindazt amit az osztályokkal lehet, ám sokkal
nagyobb erőfeszítést és figyelmet igényel.
4. Osztályok
Az osztályok implementálása nagyon hasonló a struktúrákéhoz. Lássuk az
előző programot osztállyal:
#include
using namespace std;
class Time
{
public: // bárhol elérhető
Time(); //
konstruktor
void printShort();
void printLong ();
void setTime(int, int, int);
private: // csak a tagfüggvények látják
int hour;
int minute;
int second;
};
Time::Time()
{
hour
= minute = second = 0; //inicializálás
}
void Time::printShort()
{
cout
"0" : "")
":"
"0" : "")
}
void Time::printLong()
{
cout
":"
"0" : "")
":"
"0" : "")
"
"
"AM" : "PM");
}
void Time::setTime(int
h, int m, int
s)
{
hour
= (h >= 0 && h
minute
= (m >= 0 && m
second
= (s >= 0 && s
}
int main()
{
Time
t; // a tagváltozók lenullázódnak a konstruktorban
cout
"\nRövid változat: ";
t.printShort();
cout
"\nHosszú változat: ";
t.printLong();
t.setTime(13,27,6);
cout
"\nRövid változat a setTime után:
";
t.printShort();
cout
"\nHosszú változat a setTime után:
";
t.printLong();
return 0;
}
A struktúráktól eltérően az osztályok
rendelkeznek private, public és protected specifikációkkal. Ezek a tagokhoz
való hozzáférést határozzák meg. A privát tagokhoz csak a tagfüggvények férnek
hozzá (a főprogramban érhetőek el), a
publikushoz mindenki hozzáfér aki az objektumot is eléri, a protected tagokat pedig csak az örökös osztályok érhetik el. A tagfüggvényeket közvetlenül a
deklarálásnál is lehet definiálni, de szebb, ha csak a prototípust (deklarálást) írjuk az
osztályba, a definiálást pedig azon kívül, akár a struktúrák esetében. A tagfüggvények
összessége az osztály interfészét képezi, hiszen csak azokon keresztül érhetőek
el és módosíthatók a privát tagváltozók. Publikus tagváltozókat ritkán
használunk. A tagfüggvények listájába tartozik az osztály nevével azonos nevű
függvény, a konstruktor. Ez minden alkalommal automatikusan meghívódik
valahányszor létrehozunk egy objektumot, soha nem térít vissza semmit, ezért
ebbe a függvénybe a tagváltozók inicializálását szokás beírni.
A fenti program a következőt írja ki:
Rövid változat:
00:00
Hosszú változat:
12:00:00 AM
Rövid változat a setTime után: 13:27
Hosszú változat a setTime után: 1:27:06 PM
Pluszban van a setTime interfész tagfüggvény, ami a privát tagokhoz ad
hozzáférést és közben ellenőrzi, hogy a megadott értékek a megfelelő
tartományban vannak-e. A tagfüggvények olyan módon is megírhatók, hogy azok
közvetlen hozzáférést adjanak a privát változókhoz, ez viszont helytelen taktika,
ugyanis kiiktatja a változók ellenőrzését. Tegyük be az int& badSetHour(int); függvényt a publikus tagfüggvények. Ez egy referenciát fog visszatéríteni,
a hour változó referenciáját:
int& Time::badSetHour(int
h)
{
int& ora = hour; // az "ora" a hour tagváltozó referenciája
ora =
(h >= 0 && h // ellenőrzés
return ora;
}
A hour
tagváltozó kiirásához a következő publikus tagfüggvény használható:
int Time::getHour(){ return hour; }
Ezek segítségével a main()függvény végén közvetlenül lehet módosítani a hour tagváltozót akár hibás értékkel is:
int& hourRef = t.badSetHour(20); // itt még van belső ellenőrzés
hourRef = 87; //a
hour tagváltozó módosítása - ellenőrzés nélkül
cout "\nA módosított óra: " t.getHour();
A program kimenete:
Rövid változat:
00:00
Hosszú változat:
12:00:00 AM
Rövid változat a setTime után: 13:27
Hosszú változat a setTime után: 1:27:06 PM
A módosított óra: 87
5. Kódrendezés
A programozásban általában, főként az objektum-orientált programozásban
fontos, hogy a függvényeket és azok használatát külön felületre, más fájlokba helyezzük. Az osztályok esetén az osztály deklarálását, definícióját
és alkalmazását is elválasztják egymástól. Rendszerint az osztályokat header (fejléc) fájlokban deklarálják (.h), melyet aztán az include segítségével láthatóvá lehet
tenni a program számára. A fenti programot a következőképp lehet felbontani:
time.h
#ifndef TIME_H
#define TIME_H
class Time
{
public: // bárhol elérhető
Time(); //
konstruktor
void printShort();
void printLong ();
void setTime(int, int, int);
private: // csak a tagfüggvények látják
int hour;
int minute;
int second;
};
#endif
time.cpp
#include
#include "time.h"
using std::cout;
Time::Time()
{
hour
= minute = second = 0; //inicializálás
}
void Time::printShort()
{
cout
"0" : "")
":"
"0" : "")
}
void Time::printLong()
{
cout
":"
"0" : "")
":"
"0" : "")
"
"
"AM" : "PM");
void Time::setTime(int
h, int m, int
s)
{
hour
= (h >= 0 && h
minute
= (