Oldal kiválasztása
c++

Objektumorientált Programozás (OOP) Alapjai: Az Osztályok és Objektumok Világa 🧱🧑‍💻

Képzeld el, hogy egy komplex rendszert építesz, mondjuk egy autókölcsönzőt. Hagyományos, procedurális programozással minden adatot (autó adatai, ügyfelek adatai, bérlések adatai) és minden műveletet (autó hozzáadása, bérlés rögzítése, autó törlése) külön-külön kezelnél. Ez gyorsan áttekinthetetlenné válhat.

Az OOP célja, hogy a valós világot modellezze a programban. Ahogyan a valóságban is vannak „dolgok” (objektumok), amiknek vannak tulajdonságaik (szín, méret) és viselkedésük (mozog, eszik), úgy az OOP-ban is ilyen „objektumokat” hozunk létre.

Az OOP négy alappillére:

  1. Beágyazás (Encapsulation): Az adatok és az azokon végzett műveletek egyetlen egységbe zárása.
  2. Absztrakció (Abstraction): Csak a lényeges információk mutatása, a belső részletek elrejtése.
  3. Öröklődés (Inheritance): Új osztályok létrehozása már létező osztályokból, azok tulajdonságainak és viselkedésének „öröklésével”.
  4. Polimorfizmus (Polymorphism): Különböző osztályok objektumainak egységes kezelése.

Ebben a fejezetben az első, legfontosabb lépéseket tesszük meg: megismerkedünk az osztályokkal és objektumokkal, valamint a beágyazás alapjaival.

Mi az az Osztály (class) és mi az az Objektum? 🤔

Gondolj egy osztályra (class) mint egy tervrajzra, egy sablonra vagy egy kategóriára. Egy autó esetében az „Autó” osztály írja le, hogy mi jellemző általában egy autóra: van színe, márkája, modellje, és tud gyorsulni, fékezni. Ez még nem egy konkrét autó, csak egy leírás arról, hogy mi tesz valamit autóvá.

Az objektum ezzel szemben az osztály egy konkrét példánya, egy létrehozott „dolog” a tervrajz alapján. Ha az „Autó” az osztály, akkor a „piros Audi A4-es” vagy a „kék Ford Focus” egy-egy objektum, konkrét példányai az „Autó” osztálynak.

graph TD
    A[Class: Autó] --> B{Object: Saját Autóm};
    A --> C{Object: Kolcsönzős Autó};
    A --> D{Object: Szomszéd Autója};
    B -- Tulajdonságok --> B1(szín: piros);
    B -- Tulajdonságok --> B2(márka: Audi);
    B -- Tulajdonságok --> B3(modell: A4);
    B -- Viselkedés --> B4(gyorsul());
    B -- Viselkedés --> B5(fékez());
    C -- Tulajdonságok --> C1(szín: kék);
    C -- Tulajdonságok --> C2(márka: Ford);
    C -- Tulajdonságok --> C3(modell: Focus);
    C -- Viselkedés --> C4(gyorsul());
    C -- Viselkedés --> C5(fékez());

 

Szerkezete (a struct-hoz hasonlóan):

class OsztalyNev {
public: // Ide kerülnek a publikus tagok (elérhetőek kívülről)
    // Tagváltozók (adattagok) - az objektum tulajdonságai
    Tipus1 valtozo1;
    Tipus2 valtozo2;
    // Tagfüggvények (metódusok) - az objektum viselkedései
    void muvelet1();
    Tipus3 muvelet2(ParameterTipus p);
private: 
// Ide kerülnek a privát tagok
(csak az osztályon belülről elérhetőek)   Tipus4 rejtett_valtozo;   void belso_muvelet(); }; // Fontos a pontosvessző a végén!

 

Tagváltozók és Tagfüggvények (Metódusok): Adatok és Műveletek 🗄️⚙️

Az osztályon belül kétféle dolgot definiálhatunk:

  1. Tagváltozók (adattagok): Ezek az osztályhoz tartozó változók, amik az objektum tulajdonságait tárolják. Pl. egy Autó osztályban lehet string marka, string szin, int evjarat.
  2. Tagfüggvények (metódusok): Ezek az osztályhoz tartozó függvények, amik az objektum viselkedését (műveleteit) definiálják. Pl. egy Autó osztályban lehet gyorsul(), fekez(), duda() metódus. A metódusok hozzáférhetnek az osztály tagváltozóihoz.
#include <iostream>
#include <string>
class Auto {
public: // Publikus tagok: bárki hozzáférhet
    std::string marka;
    std::string modell;
    int evjarat;
    std::string szin;
    // Tagfüggvény (metódus)
    void infoKiir() {
        std::cout << "Marka: " << marka 
<< ", Modell: " << modell                 << ", Evjarat: " << evjarat
<< ", Szin: " << szin << std::endl;   }   void gyorsul() {       std::cout << modell << " gyorsul! Vrumm-vrumm!" << std::endl;   } }; int main() {   // Objektum létrehozása az 'Auto' osztályból   Auto sajatAuto; // sajatAuto egy konkrét Auto objektum   // A tagváltozók értékének beállítása (a pont operátorral)   sajatAuto.marka = "Toyota";   sajatAuto.modell = "Corolla";   sajatAuto.evjarat = 2020;   sajatAuto.szin = "feher";   // Tagfüggvények meghívása   sajatAuto.infoKiir(); // Kiírja az autó adatait   sajatAuto.gyorsul();  // Az autó "gyorsul"   Auto kolcsonzoAuto;   kolcsonzoAuto.marka = "Skoda";   kolcsonzoAuto.modell = "Octavia";   kolcsonzoAuto.evjarat = 2022;   kolcsonzoAuto.szin = "szurke";   kolcsonzoAuto.infoKiir();   return 0; }

 

Hozzáférésmódosítók (public, private, protected): Az Adatok Védelme 🛡️

Ez az, ami megkülönbözteti a class-t a struct-tól, és ez az beágyazás egyik alapja!

  • public (publikus): Az ezen kulcsszó alá eső tagok bárhonnan elérhetők, az osztályon kívülről is. Ezek képezik az objektum „interfészét” – azokat a dolgokat, amiket a külvilág láthat és használhat.
  • private (privát): Az ezen kulcsszó alá eső tagok csak az osztályon belülről érhetők el. A külvilág számára láthatatlanok és közvetlenül nem módosíthatók. Ez a kulcsa az adatok védelmének! (Alapértelmezett, ha nem adsz meg semmit egy class esetén.)
  • protected (védett): Ez egy speciális hozzáférésmódosító, amivel később, az öröklődésnél fogunk foglalkozni. Lényegében az osztályon belülről és a belőle származtatott osztályokból érhető el.

Miért van erre szükség? (Beágyazás) A beágyazás (encapsulation) lényege, hogy az adatokat (private tagváltozók) elrejtjük a külvilág elől, és csak jól definiált metódusokon (public tagfüggvényeken) keresztül engedünk hozzájuk hozzáférést. Így elkerülhetjük, hogy a program más részeiből véletlenül vagy szándékosan érvénytelen állapotba hozzuk az objektumot. Például egy autó sebessége nem lehet negatív – egy metódus ellenőrizheti ezt, mielőtt beállítaná az értéket.

#include <iostream>
#include <string>
class Szamla {
private: 
// Privát tagváltozó
- csak az osztályon belülről elérhető   double egyenleg; public:
// Publikus tagfüggvények
- ezeken keresztül kommunikálunk az osztállyal   // Konstruktor (lásd lentebb)   Szamla(double kezdoEgyenleg) {       if (kezdoEgyenleg >= 0) {           egyenleg = kezdoEgyenleg;       } else {           egyenleg = 0; // Kezdeti egyenleg nem lehet negatív       }   }   void befizet(double osszeg) {       if (osszeg > 0) {           egyenleg += osszeg;           std::cout << "Sikeres befizetes! Uj egyenleg: "
<< egyenleg << std::endl;       } else {           std::cout << "Nem lehet negativ osszeget befizetni!"
<< std::endl;       }   }   void kivesz(double osszeg) {       if (osszeg > 0 && osszeg <= egyenleg) {           egyenleg -= osszeg;           std::cout << "Sikeres kivet! Uj egyenleg: "
<< egyenleg << std::endl;       } else if (osszeg <= 0) {           std::cout << "Nem lehet negativ osszeget kivenni!" << std::endl;       } else {           std::cout << "Nincs eleg fedezet!" << std::endl;       }   }   double getEgyenleg() { // "Getter" metódus az egyenleg lekérdezésére       return egyenleg;   } }; int main() {   Szamla bankszamla(1000.0); // Létrehozunk egy Szamla objektumot 1000 kezdőegyenleggel   // bankszamla.egyenleg = -200; // HIBA! Az 'egyenleg' privát, nem közvetlenül elérhető!   bankszamla.befizet(500.0);     // Helyes mód: metóduson keresztül   bankszamla.kivesz(200.0);   bankszamla.kivesz(2000.0);   // Nincs elég fedezet!   std::cout << "Aktualis egyenleg: "
<< bankszamla.getEgyenleg() << std::endl;   return 0; }

Láthatod, hogy az egyenleg tagváltozó privát, így közvetlenül nem módosítható. Csak a befizet(), kivesz() és getEgyenleg() publikus metódusokon keresztül kommunikálhatunk vele, amelyek gondoskodnak az adatok integritásáról.

Konstruktorok és Destruktorok: Objektumok Születése és Halála 👶👻

Minden objektumnak van egy életciklusa: létrejön, működik, majd megsemmisül. Ezt a folyamatot kezelik a konstruktorok és destruktorok.

  1. Konstruktor: Egy speciális tagfüggvény, ami automatikusan lefut, amikor egy új objektum jön létre. Feladata az objektum inicializálása, azaz a tagváltozók kezdőértékeinek beállítása.
    • Neve megegyezik az osztály nevével.
    • Nincs visszatérési típusa, még void sem!
    • Lehetnek paraméterei.
    • Lehet több konstruktor is (konstruktor túlterhelés), különböző paraméterlistákkal.

 

class Szamla {
private:
    double egyenleg;
public:
    // Paraméter nélküli (alapértelmezett) konstruktor
    Szamla() {
        egyenleg = 0.0;
        std::cout << "Szamla objektum letrehozva (alapertelmezett)."
 << std::endl;
    }
    // Paraméteres konstruktor
    Szamla(double kezdoEgyenleg) {
        if (kezdoEgyenleg >= 0) {
            egyenleg = kezdoEgyenleg;
        } else {
            egyenleg = 0.0;
        }
        std::cout << "Szamla objektum letrehozva (kezdoegyenleggel: " 
<< egyenleg << ")." << std::endl;
    }
    // ... (befizet, kivesz, getEgyenleg metódusok)
};
int main() {
    Szamla sz1;           // Meghívja az alapértelmezett konstruktort
    Szamla sz2(500.0);    // Meghívja a paraméteres konstruktort
    // ...
}

 

  1. Destruktor: Egy speciális tagfüggvény, ami automatikusan lefut, amikor egy objektum megsemmisül (például kimegy a hatókörből, vagy manuálisan töröljük delete-vel). Feladata a memóriafelszabadítás, fájlbezárás vagy egyéb „takarítás”.
    • Neve ugyanaz, mint az osztály neve, de egy hullámjellel (~) kezdődik.
    • Nincs visszatérési típusa.
    • Nincsenek paraméterei.
    • Egy osztálynak csak egy destruktora lehet.
class PeldaOsztaly {
public:
    PeldaOsztaly() {
        std::cout << "PeldaOsztaly konstruktor hivva!" << std::endl;
    }
    ~PeldaOsztaly() { // Destruktor
        std::cout << "PeldaOsztaly destruktor hivva!" << std::endl;
    }
};
int main() {
    PeldaOsztaly obj1; // Létrejön obj1, hívódik a konstruktor
    { // Új blokk, új hatókör
        PeldaOsztaly obj2; // Létrejön obj2, hívódik a konstruktor
    } // obj2 kimegy a hatókörből, hívódik a destruktora
    PeldaOsztaly* p_obj = new PeldaOsztaly(); 
// Dinamikusan létrehozott objektum, hívódik a konstruktor
    delete p_obj; // Manuálisan töröljük, hívódik a destruktor
    return 0;
} // obj1 kimegy a hatókörből, hívódik a destruktora

Ezzel a fejezettel megismerkedtünk az OOP alapjaival, az osztályokkal és objektumokkal, valamint a beágyazás koncepciójával a hozzáférésmódosítók és a konstruktorok/destruktorok segítségével. Ez egy óriási lépés a C++ programozásban!