2016. augusztus 30., kedd

Öröklés

      Az öröklés egy kód-újrahasznosítási módszer, ahol az új osztályok a már megírt osztályok alapján készülnek felhasználván azok tulajdonságait és algoritmusait, átírván némely algoritmust és létrehozván pár új tulajdonságot és algoritmust. Nem csak kódot, de időt is takarít meg, mert a már létező osztályokat nem kell újra tesztelni. Adatokat és tagfüggvényeket örökölni egy vagy több osztálytól is lehet. Mindhárom adattípust (public, private, protected) lehet örökölni, de legtöbbször a publikus öröklést használjuk. Ebben az esetben ugyanis a derivált (örökölt) osztály objektumai bárhol használhatók az ősosztály objektumaiként is. A fordított eset viszont nem igaz, azaz az ősosztály objektumai nem objektumai a derivált osztálynak is. Tulajdonképpen ez a legalapvetőbb különbség az osztályok kombinálása (amikor más osztályok objektumait használjuk tagváltozóként) és az öröklés között. A derivált osztály objektumai az ősosztály objektumaiként is kezelhetőek. Az angolban ezt „is a” (öröklés) és „has a” (kombináció) típusú osztály kapcsolatként emlegetik. Akár egy külső függvény, a derivált osztály sem fér hozzá az ősosztályának privát tagjaihoz, hacsak nincsenek ehhez hozzáférést nyújtó publikus vagy protected tagfüggvények az ősosztályban. Az öröklés egyik fölösleges tulajdonsága, hogy örökölnek a nem használt publikus tagfüggvények is. Ha az ősosztály valamely tagfüggvényének működése nem felel meg a derivált osztálynak, akkor azt újra lehet deklarálni a derivált osztályban. Az ősosztály friend függvényei nem öröklődnek. Az öröklésre példa az Absztrakció című bejegyzésben található.



1. Mutató típusú objektumok átalakítása az osztályok között
      A mutató típusú objektumokat az ősosztály és a derivált osztály között cast-tal lehet átalakítani vigyázva, hogy a mutató és az új objektum típusa találjon.

point.h
#ifndef POINT_H
#define POINT_H
#include <iostream>
using std::ostream;

class Point
{
     friend ostream& operator<<(ostream&, const Point&);

     public:
           Point(int = 0, int = 0); //konstruktor és a parametér hiányában fellépő értékek
           void setPoint(int, int); //Beállítja x és y protected tagváltozókat
           int getX() const { return x; }
           int getY() const { return y; }
     protected:                     //csak a derivált osztály érheti el
           int x, y; //egy pont koordinátái
};
#endif

point.cpp
#include <iostream>
#include "point.h"

//konstruktor - beállítja a tagváltozókat
Point::Point(int a, int b)
{
     setPoint(a, b);
}

// a tagváltozók beállítása
void Point::setPoint(int a, int b)
{
     x = a;
     y = b;
}

// Kiírja az objektumot egyéni formában
ostream& operator<<(ostream& output, const Point& p)
{
     output << '[' << p.x << ", " << p.y << ']';
    
     return output; //fűzér mód
}

circle.h
#ifndef CIRCLE_H
#define CIRCLE_H
#include <iostream>
using std::ostream;
#include <iomanip>
using std::ios;
using std::setiosflags;
using std::setprecision;
#include "point.h"

class Circle : public Point //A Point publikus deriváltja
{
     friend ostream& operator<<(ostream&, const Circle&);

     public:
           Circle(double r = 0.0, int x = 0, int y = 0);
           void setRadius(double);
           double getRadius() const;
           double area() const;
          
     protected:
           double radius;
};
#endif

circle.cpp
#include "circle.h"

// Előbb a Point konstruktorát hívja meg
Circle::Circle(double r, int a, int b) : Point(a, b)
{
     setRadius(r); // és csak utána állítja be a saját tagváltozóit
}

void Circle::setRadius(double r)
{
     radius = (r > 0 ? r : 0); //a sugár csak pozitív lehet
}

double Circle::getRadius() const
{
     return radius;
}

double Circle::area() const
{
     return 3.14159 * radius * radius;
}

// Kiírja az objektumot egyéni formában: Központ = [x, y]; Sugár = #.##
ostream& operator<<(ostream& output, const Circle& c)
{
     output << "Központ = " << static_cast<Point>(c)<< ";";
     output << "Sugár = " << setiosflags(ios::fixed | ios::showpoint)
                                 << setprecision(2) << c.radius;
     return output;
}

test_point_circle.cpp
#include <iostream>
using std::cout;
using std::endl;
#include <iomanip>
#include "circle.h"

int main()
{
     Point *pointPtr = 0, p(30, 50);
     Circle *circlePtr = 0, c(2.7, 120, 89);

     cout << "A p pont: " << p << endl;
     cout << "A c kör: " << c << endl;
    
     //a Circle objektum címét egy Point típusó mutatóba írja
     pointPtr = &c; //upcasting
     cout << "\nA c kör a Point osztály szemszögébôl: " << *pointPtr << endl;
    
     //A Circle mutató felveszi a Point mutató értékét, ami Circle objektumra mutat
     circlePtr = static_cast<Circle*>(pointPtr); //downcasting
     cout << "\nA c kör a Circle osztály szemszögébôl:\n" << *circlePtr << endl;
    
     //területszámítás
     cout << "A c kör területe: circlePtr->area() = " << circlePtr->area() << endl;
    
     //a Point objektum címét egy Point típusó mutatóba írjuk
     pointPtr = &p;
     //A Circle mutató felveszi a Point mutató értékét, ami Point objektumra mutat
     circlePtr = static_cast<Circle*>(pointPtr);
     cout << "\nA p pont a Circle osztály szemszögébôl:\n" << *circlePtr << endl;
    
     //teruletszámítás
     cout << "A c kör területe: circlePtr->area() = " << circlePtr->area() << endl;
    
     return 0;
}
A kimenet:
A p pont: [30, 50]
A c kör: Központ = [120, 89];Sugár = 2.70

A c kör a Point osztály szemszögébôl: [120, 89]

A c kor a Circle osztály szemszögébôl:
Központ = [120, 89];Sugár = 2.70
A c kör területe: circlePtr->area() = 22.90

A p pont a Circle osztály szemszögébôl:
Központ = [30, 50];Sugár = 0.00
A c kör területe: circlePtr->area() = 0.00

A Point ősosztály publikus interfésze tartalmazza a setPoint, getX és getY tagfüggvényeket. Az x és y tagváltozók viszont protected típusúak, hogy az osztály közvetlen felhasználói ne férjenek hozzá, de a derivált osztályok igen. Ha private típusúak lennének, akkor a derivált osztály is csak a Point publikus függvényeivel érhetné el. A Circle osztály a Point ősosztály publikus deriváltja: class Circle : public Point. Ez azt jelenti, hogy a Point minden public és protected tagja átöröklődött a Circle osztályba. Azt is jelenti, hogy a Circle publikus interfésze nem csak az area, setRadius és getRadius tagfüggvényekből áll, hanem magába foglalja a Point ősosztály publikus tagfüggvényeit is. Az öröklésnél fontos, hogy a konstruktorok a megfelelő sorrendben hívódjanak meg. Előbb az ős és utána az utód tagváltozóit kell inicializálni. Legegyszerűbb a paraméterlista után kettősponttal jelezni, hogy a paraméterek közül melyek inicializálják a Pointer osztály tagváltozóit: Circle::Circle(double r, int a, int b) : Point(a, b). Ebben az esetben az a és a b lesz amit a Point konstruktor megkap. Ha ezt nem írjuk oda, akkor is a Point konstruktor fut le hamarább, csakhogy az inicializált értékekkel, melyek ebben az esetben nullák: Point(int = 0, int = 0) . Ha ez az inicializálás sincs megírva, akkor a fordító hibát jelez. A Point *pointPtr = 0, p(30, 50); programkód létrehoz egy NULL mutatót és egy p objektumot. Ugyanígy készül a Circle objektum is. Mindét osztálynak van egy operátorfüggvénye, ami túlterheli a << operátort és saját stílusban írja ki az objektumokat. A Circle derivált osztály használhatja a Point ősosztály operátorfüggvényét is, ám az csak a saját maga által ismert adatokat tudja kiírni. Hogy a Point operátorfüggvénye hívódjon meg, Point típusú mutatót kell kiíratni, tehát a pointPtr fel kell vegye a Circle típusú objektum címét. A pointPtr = &c; műveletet upcasting-nak nevezik, mert a derivált objektumot kezeljük ősobjektumként. Ez azt jelenti, hogy csak azok az adatok elérhetőek az objektumból amelyek az ősosztályból valók, ebben az esetben az x és y koordináta. Ennek ellenkezője a downcasting: circlePtr = static_cast<Circle*>(pointPtr);. Az upcasting esetén a Circle objektum mutatója Point típusú lett. A downcasting esetén visszaalakítás történik, melyben az értéket csakis egy Circle típusú mutató veheti fel. A program végül megadja a Point objektum címét a pointPtr mutatónak, majd átalakítja Circle mutatóvá. Ennek eredménye, hogy kiíráskor a Circle operátorfüggvénye fog meghívódni és nem fogja tudni, hogy mennyi a Point objektum területe és sugara, ezért az inicializált nullákkal helyettesíti az ismeretlen adatokat.

      Annak ellenére, hogy a derivált objektum az ősosztályból származik, típusa mégis különbözhet. A publikus öröklés esetén a derivált objektumokat lehet ősi objektumként kezelni, hiszen a tagok hozzáférési típusa nem változik. Az öröklés célja, hogy az alaptulajdonságok mellett új tulajdonságokat soroljunk fel, ezért a derivált osztály mindig több taggal rendelkezik mint az ősosztály. Az ősosztályban ezért is nem lehet a derivált objektumokra hivatkozni, mert az ismeretlen tagok definiálatlanul maradnának. A publikus öröklés lehetővé teszi, hogy a derivált osztály mutatója átalakítható legyen, hogy az ősosztály objektumára mutasson, ahogyan ezt a fenti példában láttuk. Tulajdonképpen négyféleképp kombinálhatóak a mutatók és az objektumok a derivált és az ősosztály között:
1. Az ősobjektumra való hivatkozás egy ősmutató segítségével
pointPtr = &p;
      2. A derivált objektumra való hivatkozás egy derivált mutató segítségével
circlePtr = &c;
      3. A derivált objektumra való hivatkozás egy ősmutató segítségével. Ez lehetséges, hiszen a derivált objektum az ősosztály objektuma is, viszont csak az ősosztály tagjait használhatjuk, különben a fordító hibát jelez.
     pointPtr = &c;
4. Az ősobjektumra való hivatkozás egy derivált mutató segítségével. Közvetlenül nem lehet, csak ha átalakítjuk a derivált mutatót ősmutatóvá.
     circlePtr = static_cast<Circle*>(pointPtr);


2. Tagfüggvények használata
      A derivált és az ősosztályok tagfüggvényei között olykor csak kevés eltérés van, mint például az előző példában szereplő operátorfüggvények esetén. Ilyenkor nem érdemes a függvény nevét megváltoztatni, ugyanis felülírható az ősosztály tagfüggvénye. Egyszerűen újra kell deklarálni az adott függvényt a derivált osztályba, és onnantól az lesz az elsődlegesen érvényes függvény. Továbbra is használható marad az ősosztály tagfüggvénye, ám használni kell a tartományoperátort.

employee.h
#ifndef EMPLOYEE_H
#define EMPLOYEE_H

class Employee
{
     public:
           Employee(const char*, const char*);
           void print() const// Kiírja a vezeték- és keresztnevet
           ~Employee();
     private:
           char* firstName;//dinamikusan lefoglalt string
           char* lastName; //dinamikusan lefoglalt string
};

#endif

employee.cpp
#include <iostream>
using std::cout;
#include <cstring>
#include <cassert>
#include "employee.h"

Employee::Employee(const char* first, const char* last)
{
     firstName = new char[strlen(first) + 1]; // dinamikus memórialefoglalás
     assert(firstName != 0);                  // abort ha nem sikerül lefoglalni
     strcpy(firstName, first);                // bemásolja a bejövő paramétert a tagváltozóba

     lastName = new char[strlen(last) + 1];
     assert(lastName != 0);
     strcpy(lastName, last);
}

void Employee::print() const
{
     cout << firstName << ' ' << lastName;
}

Employee::~Employee()
{
     delete[] firstName; //memória felszabaditás
     delete[] lastName;  //memória felszabaditás
}

hourly.h
#ifndef HOURLY_H
#define HOURLY_H
#include "employee.h"

class HourlyWorker : public Employee
{
     public:
           HourlyWorker(const char*,const char*,double, double);
           double getPay() const;
           void print() const; //az ososztály tagfüggvényének felülírása
     private:
           double wage;
           double hours;
};

#endif

hourly.cpp
#include <iostream>
using std::cout;
using std::endl;
#include <iomanip>
using std::ios;
using std::setiosflags;
using std::setprecision;
#include "hourly.h"

HourlyWorker::HourlyWorker(const char* first, const char* last,
   double initHours, double initWage) : Employee(first, last)
{
     hours = initHours;
     wage = initWage;
}

//A munkások fizetése
double HourlyWorker::getPay() const
{
     return wage*hours;
}

//A felülírt függvény: kiírja a fizetéseket is
void HourlyWorker::print() const
{
     cout << "HourlyWorker::print():" << endl;
     Employee::print(); //az eredeti függvény
     cout << " egy munkás ember havi "
     << setiosflags(ios::fixed | ios::showpoint)
     << setprecision(2) << getPay() << " EUR minimálbérrel." << endl;
}

test_hourlyworker.cpp
#include"hourly.h"
int main()
{
     HourlyWorker h("Bob", "Smith", 40.0, 10.00);
     h.print();
    
     return 0;
}

A kimenet:
HourlyWorker::print():
Bob Smith egy munkás ember havi 400.00 EUR minimálbérrel.

      Az Employee osztály definíciója két char* privát tagváltozóból és három tagfüggvényből áll. A konstruktor két karakterláncot kap paraméternek, melyeknek dinamikusan lefoglal egy-egy char táblát a memóriában. Ha a helylefoglalás sikertelen, az assert hibaüzenettel lépik ki a programból. Használható a try/catch szekvencia is, mely bad_alloc kivételt generál ha a new nem működött megfelelően. Mivel az Employee osztály tagváltozói (vagy adattagjai) privátak, az egyetlen módszer az elérésükhöz a print függvény, amely kiírja őket. Az osztály destruktora felszabadítja a tagváltozóknak dinamikusan lefoglalt memóriát elkerülve ezzel a memory leak nevű jelenséget.
      Az HourlyWorker osztály az Employee osztályból van származtatva publikus típusú örökléssel. Felülírja a print az ősosztályban is létező print függvényt, így két print függvényhez van hozzáférése. Ilyen esetben általában a derivált változat alkalmazza az ősi változatot is, hogy kiírja az ott elérhető információkat, ehhez azonban eléje kell írja az ősosztály nevét: Employee::print();


3. A public, protected és private típusú tagok öröklődése
      Az osztályok származtatásakor az ősosztályt lehet public, private vagy protected típusú örökléssel származtatni. A private és protected nagyon ritka, inkább a public típust használjuk. A következő táblázatban fel van sorolva mindenik hozzáférési típusú származtatás és hogy mihez ad hozzáférést az ősosztályból.

Az ősosztály tagjainak hozzáférési típusa, amelyet a derivált osztály elérhet
Öröklés típusa

public

protected

private



public
Az ősosztály public tagjai public hozzáférésűek a derivált osztályban is. Közvetlenül elérhetőek bármilyen nem statikus tagfüggvénnyel vagy osztályon kívüli friend függvénnyel.
Az ősosztály public tagjai protected hozzáférésűek a derivált osztályban. Közvetlenül elérhetőek bármilyen nem statikus tagfüggvénnyel vagy osztályon kívüli friend függvénnyel.
Az ősosztály public tagjai private hozzáférésűek a derivált osztályban. Közvetlenül elérhetőek bármilyen nem statikus tagfüggvénnyel vagy osztályon kívüli friend függvénnyel.



protected
Az ősosztály protected tagjai protected hozzáférésűek a derivált osztályban is. Közvetlenül elérhetőek bármilyen nem statikus tagfüggvénnyel vagy osztályon kívüli friend függvénnyel.
Az ősosztály protected tagjai protected hozzáférésűek a derivált osztályban is. Közvetlenül elérhetőek bármilyen nem statikus tagfüggvénnyel vagy osztályon kívüli friend függvénnyel.
Az ősosztály protected tagjai private hozzáférésűek a derivált osztályban. Közvetlenül elérhetőek bármilyen nem statikus tagfüggvénnyel vagy osztályon kívüli friend függvénnyel.



private
Az ősosztály private tagjai nem elérhetőek a derivált osztályban. Közvetlenül elérhetőek bármilyen nem statikus tagfüggvénnyel vagy osztályon kívüli friend függvénnyel, ha az eléréshez az ősosztály függvényeit használjuk.
Az ősosztály private tagjai nem elérhetőek a derivált osztályban. Közvetlenül elérhetőek bármilyen nem statikus tagfüggvénnyel vagy osztályon kívüli friend függvénnyel, ha az eléréshez az ősosztály függvényeit használjuk.
Az ősosztály private tagjai nem elérhetőek a derivált osztályban. Közvetlenül elérhetőek bármilyen nem statikus tagfüggvénnyel vagy osztályon kívüli friend függvénnyel, ha az eléréshez az ősosztály függvényeit használjuk.

Látható, hogy a public típusú öröklésnél minden public tag public marad, a protected pedig protected a származtatott osztályban is. A protected öröklés mindent protected típussá tesz, a private öröklés pedig mindent private típussá. Ez utóbbival az a baj, hogy minden tagfüggvény az ősosztályból segédfüggvénnyé válik a derivált osztályban. Egyik típusú öröklés sem enged hozzáférést az ősosztály private hozzáférésű tagjaihoz. Ha a derivált osztályból is deriválunk osztályokat, akkor nem kell a legősibb osztályig felsorolni mindent, csupán azt az osztályt ami egy absztrakciós szinttel felette van. Ebben az esetben a legősibb osztály közvetett őse a derivált osztálynak.


4. A derivált osztályok konstruktorai és a destruktorai
      A derivált osztály objektumai létrehozásakor az ősosztály konstruktorai futnak le előbb. Ezek és az operátorfüggvények nem öröklődnek át, viszont használhatóak a derivált osztály konstruktoraiban és operátor függvényeiben. A destruktorok fordított sorrendben hívódnak meg, azaz előbb a derivált osztály objektumai bomlanak le, aztán az ősosztály objektumai.
      Tegyük fel, hogy az ősosztály és a derivált osztály is egy másik, „idegen” osztály objektumait tartalmazza. Amikor létrehozunk egy objektumot a derivált osztályban, először az ősosztályban lévő „idegen” objektumok konstruktora hívódik meg, majd az ősosztály konstruktora, majd a derivált osztályban lévő „idegen” objektumok konstruktora, végül a derivált osztály konstruktora. A destruktorok fordított sorrendben futnak le.

point2.h
#ifndef POINT2_H
#define POINT2_H
class Point
{
     public:
           Point(int = 0, int = 0);
           ~Point();
     protected: //csak a derivált osztály látja
           int x, y;
};
#endif

point2.cpp
#include <iostream>
using std::cout;
using std::endl;
#include "point2.h"

Point::Point(int a, int b)
{
     x = a;
     y = b;
     cout << "Point konstruktor: "
            << '[' << x << ", " << y << ']' << endl;
}

Point::~Point()
{
     cout << "Point destruktor: "
            << '[' << x << ", " << y << ']' << endl;
}

circle2.h
#ifndef CIRCLE2_H
#define CIRCLE2_H
#include "point2.h"
class Circle : public Point
{
     public:
           Circle(double r = 0.0, int x = 0, int y = 0);
           ~Circle();
     protected:
           double radius;
};
#endif

circle2.cpp
#include <iostream>
using std::cout;
using std::endl;
#include "circle2.h"

Circle::Circle(double r, int a, int b) : Point(a, b)
{
     radius = r;
     cout << "Circle konstruktor: a sugár: "
            << radius << " [" << x << ", " << y << ']' << endl;
}

Circle::~Circle()
{
     cout << " Circle destruktor: a sugár: "
            << radius << " [" << x << ", " << y << ']' << endl;
}

test_point2_circle2.cpp
#include <iostream>
using std::cout;
using std::endl;
#include "circle2.h"

int main()
{
     //Konstruktor és destruktor teszt
     {
           Point p(11, 22);
     }
     cout << endl;
    
     Circle circle1(4.5, 72, 29);
     cout << endl;
    
     Circle circle2(10, 5, 5);
     cout << endl;
    
     return 0;
}

A kimenet:
Point konstruktor: [11, 22]
Point destruktor: [11, 22]

Point konstruktor: [72, 29]
Circle konstruktor: a sugár: 4.5 [72, 29]

Point konstruktor: [5, 5]
Circle konstruktor: a sugár: 10 [5, 5]

Circle destructor: a sugár: 10 [5, 5]
Point destruktor: [5, 5]
Circle destructor: a sugár: 4.5 [72, 29]
Point destruktor: [72, 29]

A Circle osztály a Point publikus derivált osztálya, melynek van egy radius tagváltozója az örökölt tagváltozók mellett. A Circle konstruktora meghívja a Point konstruktorát, hogy inicializálja az ősosztály x és y tagváltozóit. A főprogramban a p objektum külön blokkban van deklarálva, így amikor a blokk végére ér, meghívódik a Point destruktora. Ezek után a circle1 és circle2 objektumok következnek, melyeknél látható, hogy előbb a Point konstruktora fut le.


5. A „uses a” és a „knows a” típusú kapcsolat
      Az osztályok öröklése és a kombinálása a kód-újrahasznosítást szorgalmazza olyan osztályok révén, melyeknek közös tagjaik vannak. Ahogyan egy „személy” típusú objektum nem „jármű” típusú objektum ugyanúgy nem is lehet annak leszármazottja vagy őse sem. A „személy” objektumnak viszont használhat egy (uses a) „jármű” objektumot. Egy objektum egy mutatóval, referenciával vagy közvetlen módon használhat egy másik objektumot és annak nem-privát tagfüggvényeit. Egy objektum tudhat egy (knows a) másik objektum létezéséről, azaz tartalmazhat egy mutatót vagy referenciát egy másik objektumra. A különbség a „használ” és „tud róla” között, hogy míg a „használ” esetben mindkét objektum létezése azonos időtartamú, addig a „tud róla” esetben az objektumok egymástól függetlenül is létezhetnek. A következő példában a Cylinder és a Circle osztályok a Point osztályból származnak közvetetten és közvetlenül.

point.h
#ifndef POINT_H
#define POINT_H
#include <iostream>
using std::ostream;

class Point
{
     friend ostream& operator<<(ostream&, const Point&);

     public:
           Point(int = 0, int = 0);
           void setPoint(int, int);
           int getX() const { return x; }
           int getY() const { return y; }

     protected:
           int x, y;
};

#endif

point.cpp
#include <iostream>
#include "point.h"

Point::Point(int a, int b)
{
     setPoint(a, b);
}

void Point::setPoint(int a, int b)
{
     x = a;
     y = b;
}

ostream& operator<<(ostream& output, const Point& p)
{
     output << '[' << p.x << ", " << p.y << ']';
     return output; //fűzér mód
}

circle.h
#ifndef CIRCLE_H
#define CIRCLE_H
#include <iostream>
using std::ostream;
#include <iomanip>
using std::ios;
using std::setiosflags;
using std::setprecision;
#include "point.h"

class Circle : public Point
{
     friend ostream& operator<<(ostream&, const Circle&);

     public:
           Circle(double r = 0.0, int x = 0, int y = 0);
           void setRadius(double);
           double getRadius() const;
           double area() const;
    
     protected:
           double radius;
};

#endif

circle.cpp
#include "circle.h"

Circle::Circle(double r, int a, int b) : Point(a, b)
{
     setRadius(r);
}

void Circle::setRadius(double r)
{
     radius = (r > 0 ? r : 0);
}

double Circle::getRadius() const
{
     return radius;
}

double Circle::area() const
{
     return 3.14159 * radius * radius;
}

ostream& operator<<(ostream& output, const Circle& c)
{
     output << "A kör közepe = " << static_cast<Point>(c)
              << "\nA kör suagara = "
              << setiosflags(ios::fixed | ios::showpoint)
              << setprecision(2) << c.radius;
     return output; //fűzér mód
}

cylinder.h
#ifndef CYLINDER_H
#define CYLINDER_H
#include <iostream>
using std::ostream;
#include "circle.h"

class Cylinder : public Circle
{
    
     friend ostream& operator<<(ostream&, const Cylinder&);

     public:
           Cylinder(double h = 0.0, double r = 0.0,
           int x = 0, int y = 0);
           void setHeight(double);
           double getHeight() const;
           double area() const;
           double volume() const;

     protected:
           double height;
};
#endif

cylinder.cpp
#include "cylinder.h"

Cylinder::Cylinder(double h, double r, int x, int y) : Circle(r, x, y)
{
     setHeight(h);
}

void Cylinder::setHeight(double h)
{
     height = (h >= 0 ? h : 0);
}

double Cylinder::getHeight() const
{
     return height;
}

double Cylinder::area() const
{
     return 2 * Circle::area() +
     2 * 3.14159 * radius * height;
}

double Cylinder::volume() const
{
     return Circle::area() * height;
}

ostream& operator<<(ostream& output, const Cylinder& c)
{
     output << static_cast<Circle>(c)
              << "\nA henger magassága = " << c.height;
     return output; //fűzér mód
}

test_cylinder.cpp
#include<iostream>
using std::cout;
using std::endl;
#include "cylinder.h"

int main()
{
     Cylinder cyl(5.7, 2.5, 12, 23);

     cout << "x = " << cyl.getX()      // A Point interfészből
            << "\ny = " << cyl.getY()  // A Point interfészből
            << "\nsugár = " << cyl.getRadius() //A Circle interfészből
            << "\nmagasság = " << cyl.getHeight() << "\n\n"; // A sajat interfészből

     cyl.setHeight(10);   // A saját interfészből
     cyl.setRadius(4.25); // A Circle interfészből
     cyl.setPoint(2, 2);  // A Point interfészből

     cout << "cyl:\n" << cyl << '\n';
     cout << "A henger területe: " << cyl.area() << '\n'; // A saját interfészből

     Point& pRef = cyl;

     cout << "\nA cyl a Point osztály szemszögéből: " << pRef << "\n\n";
    
     Circle& circleRef = cyl;

     cout << "A cyl a Circle osztály szemszögéből:\n" << circleRef << '\n';
     cout << "A henger területe: " << circleRef.area() << endl;

     return 0;
}
A kimenet:
x = 12
y = 23
sugár = 2.5
magasság = 5.7

cyl:
A kör közepe = [2, 2]
A kör suagara = 4.25
A henger magassága = 10.00
A henger területe: 380.53

A cyl a Point osztály szemszügéből: [2, 2]

A cyl a Circle osztály szemszögéből:
A kör közepe = [2, 2]
A kör suagara = 4.25
A kör területe: 56.74

A Cylinder  osztály a Circle osztály deriváltja, amely a Point osztály deriváltja. Ez azt jelenti, hogy a Point és a Cyrcle interfésze elérhető a Cylinder osztályban, amely még hozzáteszi a setHeight, getHeight, volume és area tagfüggvényeket. Ezek közül az area megtalálható a Circle osztályban is, azonban felülíródik. A Cylinder konstruktora meg kell hívja a Circle konstruktorát. A Point konstruktorát a Circle konstruktora hívja meg. A derivált osztály konstruktora csak a közvetlen ősosztály konstruktorának meghívásáért felelős. A főprogramban a cyl egy Cylinder típusú objektum, melyre az összes létező interfészt alkalmazva van. Ezután deklarálva van egy Point típusú referencia, mely a cyl objektumra hivatkozik: Point& pRef = cyl;. A pRef így az objektumból csak a Point tudásának eleget tevő koordinátákat látja. Hasonló a helyzet a Circle& circleRef = cyl; esetben is, ahol a circleRef referencia csak a Circle által ismert adatokat tudja kiírni. Ebben az esetben az area az eredeti és nem a felülírt függvényt fogja jelenti. A szemszögek kiírásához ugyanilyen módon jó lett volna egy print() függvény is, azonban az << operátor átdefiniálása olvashatóbbá teszi a kódot.


6. A többszörös öröklés
      A derivált osztálynak több ősosztálya is lehet. A következő példában a Derived osztály a Base1 és a Base2 osztályok leszármazottja.

base1.h
#ifndef BASE1_H
#define BASE1_H
class Base1
{
     public:
           Base1(int x){ value = x; }
           int getData() const { return value; }
     protected:
           int value;
};
#endif

base2.h
#ifndef BASE2_H
#define BASE2_H
class Base2
{
     public:
           Base2(char c){ letter = c; }
           char getData() const { return letter; }
     protected:
           char letter;
};
#endif

derived.h
#ifndef DERIVED_H
#define DERIVED_H
#include <iostream>
using std::ostream;
#include "base1.h"
#include "base2.h"

class Derived : public Base1, public Base2
{
     friend ostream& operator<<(ostream &, const Derived&);

     public:
           Derived(int, char, double);
           double getReal() const;

     private:
           double real;
};

#endif

derived.cpp
#include "derived.h"

Derived::Derived(int i, char c, double f) : Base1(i), Base2(c), real(f)
{}//az f paramétert a real privát tagváltozóba teszi

double Derived::getReal() const
{
     return real;
}

ostream& operator<<(ostream& output, const Derived& d)
{
     output << "value = " << d.value        //A Base1-ből
              << "\nletter = " << d.letter  //A Base2-ből
              << "\nreal = " << d.real;     //A sajátból
    
     return output;
}

test_derived.cpp
#include <iostream>
using std::cout;
using std::endl;
#include "derived.h"

int main()
{
     Base1 b1(10), *base1Ptr = 0;
     Base2 b2('Z'), *base2Ptr = 0;
     Derived d(7, 'A', 3.5);

     cout << "b1 tartalma: " << b1.getData()
            << "\nb2 tartalma: " << b2.getData()
            << "\nd tartalma:\n" << d << "\n\n";
            
     cout << "A Derived tagjai egyenként is elérhetőek:"
            << "\nvalue: " << d.Base1::getData()
            << "\nletter: " << d.Base2::getData()
            << "\nreal: " << d.getReal() << "\n\n";
            
     cout << "d mint az ősosztályok objektuma:\n";
    
     base1Ptr = &d; // A Base1 objektuma
     cout << "base1Ptr->getData() = " << base1Ptr->getData() << '\n';

     base2Ptr = &d; // A Base2 objektuma
     cout << "base2Ptr->getData() = " << base2Ptr->getData() << '\n';

     return 0;
}

A kimenet:
b1 tartalma: 10
b2 tartalma: Z
d tartalma:
value = 7
letter = A
real = 3.5

A Derived tagjai egyenként is elérhetőek:
value: 7
letter: A
real: 3.5

d mint az ősosztályok objektuma:
base1Ptr->getData() = 7
base2Ptr->getData() = A

A többszörös öröklés az ősosztályok felsorolása révén valósul meg: class Derived : public Base1, public Base2. A derivált osztály konstruktorában mindenik ősosztály konstruktorát meg kell hívni, hogy inicializálódjanak az onnan örökölt értékek is. A főprogram mindenik osztálytípusból deklarál egy-egy objektumot, majd kiírja ezek tartalmát a beépített tagfüggvények és az operátorfüggvény segítségével. A tagfüggvények nevei nem téveszthetők össze, hisz más-más objektumhoz tartoznak. Amikor egyenként vannak kiírva, akkor is ott áll az adatok előtt az osztályazonosító (pl. d.Base1::getData()). Ha egyedi nevük lenne, nem lenne szükség osztályazonosítóra. Végül pedig két upcasting látható az ősosztályokra, valamint a mutatók alkalmazása a függvények megkülönböztetésére.