Polimorfizmus

(toc)


      A polimorfizmus és a virtuális függvények segítségével olyan szoftverek
fejleszthetők, melyek könnyű módszerrel bővíthetőek. Ezek a programok az
osztály hierarchia minden objektumát az ősosztály objektumaiként kezelik, így
az újonnan hozzáadott osztályok objektumait is rögtön kezelni tudják. Csak
azoknál a programrészeknél kell módosítást végrehajtani, melyek specifikus
információkat használnak az újonnan hozzáadott osztályról.





Switch


     
A különböző adattípusok használatára a
switch az egyik alternatív módszer, hiszen ezzel
más-más utasításokat lehet végrehajtatni az objektum típusának függvényében.
Például a formák osztályának hierarchiájában kiválasztható, hogy a melyik
típusú formához melyik print() függvényt hívjuk meg. A
switch-el azonban problémák is vannak. A
programozó kifelejthet egy lehetséges esetet, például mikor egy új osztállyal
bővíti a programot elfelejti beírni annak print() esetét a switch-be. Ugyanígy
ha megváltoztatunk vagy kitörlünk egy osztályt, akkor mindenik switch-ből ki
kell törölni annak esetét és, különben egy hibákkal teli program lesz az
eredmény.





Virtuális függvények


     
A
switch logikájának
automatizálását a virtuális függvények valósítják meg. Legyen egy
Shape ősosztály, melynek deriváltjai a Point, Circle, Triangle, Rectangle és Square. Mindenik osztálynak van egy printShapeName függvénye, mely az adott forma nevét írja
ki, ezért különbözőek. Jó lenne mégis, ha mindenik forma azonos módon kezelhető
lenne, hisz egy ősosztályból származnak. Más szóval, ha az ősosztály szintjén
meghívjuk a
printShapeName függvényt,
akkor a program dinamikus módon (futás közben) határozza meg, melyik derivált
osztály függvényéről van szó. Ehhez a
printShapeName függvényt virtuálisnak kell deklarálni az
ősosztályban, és mindenik örökös felül kell írja a maga módján.





#include <iostream>


using namespace std;





class Shape{


     public:


           virtual void
printShapeName() const{


                cout
<< "Shape::printShapeName()"
<< endl;


           }


           void init() const{


                cout
<< "Shape::init()" <<
endl;


           }


};





class Point : public
Shape{


     public:


           void printShapeName() const{


                cout
<< "Point::printShapeName()"
<< endl;


           }


           void init() const{


                cout
<< "Point::init()" <<
endl;


           }


};





int main()


{


     Shape
shape;


     cout
<< "A Shape függvényei Shape
objektummal:"
<< endl;


     shape.printShapeName();


     shape.init();


    


     Shape*
shapePtr = new Shape();


     cout
<< "\nA Shape függvényei Shape
mutatóval:"
<< endl;


     shapePtr->printShapeName();


     shapePtr->init();


    


     Point*
pointPtr = new Point();


     shapePtr
= pointPtr;


     cout
<< "\nA Shape függvényei Point mutatóval,
1. eset:"
<< endl;


     shapePtr->printShapeName();
//polimorfizmus


     shapePtr->init();           //nem polimorfizmus


    


     Point
point;


     shapePtr
= &point;


     cout
<< "\nA Shape függvényei Point mutatóval,
2. eset:"
<< endl;


     shapePtr->printShapeName();
//polimorfizmus


     shapePtr->init();           //nem polimorfizmus


    


     return 0;


}





A kimenet:


A Shape
fuggvenyei Shape objektummal:


Shape::printShapeName()


Shape::init()





A Shape
fuggvenyei Shape mutatoval:


Shape::printShapeName()


Shape::init()





A Shape
fuggvenyei Point mutatoval, 1. eset:


Point::printShapeName()


Shape::init()





A Shape fuggvenyei
Point mutatoval, 2. eset:


Point::printShapeName()


Shape::init()





Az ősosztály printShapeName függvénye mutatóval vagy a pont
operátorral is elérhető, akár egy sima függvény. Ugyanez érvényes a derivált
osztály függvényeire is. Ha viszont az ősosztály típusú mutató egy derivált
osztály objektumára mutat (
shapePtr = pointPtr vagy shapePtr = &point),
akkor a program automatikusan derivált osztályban lévő
printSapeName függvényt veszi számításba (merthogy az
ősosztályban virtuálisnak volt deklarálva). Ezt angolul „dynamic binding” azaz
dinamikus csatolásnak vagy köteléknek nevezik.





Absztrakt ősosztályok


     
Amikor az osztályra, mint adattípusra gondolunk, akkor arra számítunk,
hogy az osztályt használó alkalmazásban lesznek majd ilyen típusú objektumok.
Vannak viszont olyan körülmények, mikor az adott osztálynak nem deklarálunk
objektumokat. Ezek az absztrakt osztályok és általában ősosztályok, ezért
absztrakt ősosztályoknak hívjuk. Az ilyen osztályokból tehát nem deklarálunk
objektumokat, ellenben ennek deriváltjaiból már igen, és ezért őket konkrét
osztályoknak hívjuk. Az absztrakt osztályok túl alaposztályok, túl általánosak
ahhoz, hogy bármit is kezdeni lehessen az objektumaival. Az ősosztály akkor lesz
absztrakt, hogyha egy vagy több virtuális függvénye tiszta. Ez azt jelenti,
hogy nincsen tartalma és ezt az
=0
taggal jelzik a deklarálásnál:
virtual
void earnings() const = 0;
. Ha ezt a függvényt a derivált osztály se írja felül, akkor ő is
absztrakt osztálynak számít.





Polimorfizmus


     
Lehetővé teszi, hogy a különböző osztályok objektumai, melyeket öröklés
köt össze, másképp válaszoljanak az adott függvényre. Ezt a virtuális
függvények teszik lehetővé, amint azt a fenti példában is láttuk. Természetesen
a nem virtuális függvények is felülírhatók, erre ad példát az
init() függvény. Minden esetben az ősosztály tagfüggvénye hívódott
meg, mert bár a
sharePtr a Point objektumra mutat, a típusa akkor is Shape. A típusátalakítás nem
segíthet, hiszen nem a típus miatt nem működik, hanem a kiírás miatt, ugyanis minden
esetben a Shape szemszögéből tesszük:
shapePtr->init();. Ebben az esetben nincs polimorfizmus. A polimorfizmus elősegíti a program
bővíthetőséget. Az ilyen stílusban írt alkalmazás független azoktól az
objektumoktól akiknek üzenetet küld, azaz nem kell figyeljen arra, hogy a megfelelő
tagfüggvény hívódjék meg és ezért nem is kell módosítani a program
alapszerkezetét. Az új osztályok vagy objektumok hozzáadásakor nem kell újra
végigmenni a teljes kódon, csak egyszerűen újra kell fordítani a fordító
segítségével. Ha absztrakt ősosztályok is vannak a kódban, akkor nem
deklarálhatunk nekik objektumokat, viszont mutatókat és referenciákat igen.
Ezek felhasználhatók a derivált objektumok polimorfikus kezelésére. A
polimorfizmus akkor is hasznosnak bizonyul, ha a fejlesztő nem tudja előre,
hogy milyen osztályok kellenek majd a program végső verziójáig, mert új
osztályokat a dynamic binding segítségével rögtön alkalmazni tudja majd. A
virtuális függvényt használó objektum típusa nem fontos a fordítás során, majd
a futásnál a virtuális függvény az őt használó objektum tagfüggvényeként lesz
azonosítva.





Virtuális destruktorok


     
Amikor polimorfizumst használunk a dinamikusan lefoglalt objektumokon,
akkor problémák léphetnek fel azok lebontásánál. Amikor egy derivált objektumot,
amelyet éppen az ősosztály használ, explicit módon lebontunk a
delete operátorral, akkor az ősosztály
destruktora hívódik meg attól függetlenül, hogy milyen típusú objektumról van
szó. Hogy ez ne történjen meg, az ősosztály destruktorát is virtuálisnak
deklaráljuk. Ettől aztán az összes derivált osztály destruktora virtuálissá
válik még akkor is, ha más nevük van. A destruktorokkal ellentétben, a
konstruktorokat nem lehet virtuálisnak deklarálni, de nincs is amiért.





Példa


     
A következő programban a
Shape
ősosztálynak a
Point,
a
Circle és a Cylinder lesznek a leszármazottjai. A Shape absztrakt ősosztály, mert a printShapeName és a print virtuális függvényei tiszták. Van még két
virtuális függvénye, az
area és
a
volume, amelyek nullát térítenek vissza, azaz
önmagukban nem sokat érnek, de a
Point
osztálynak éppen megfelelnek, hiszen úgysem veszi hasznukat. A
Circle a Point leszármazottja lesz és felülírja az area függvényt. A Cylinder a Circle leszármazottja és
ő felülírja a
volume függvényt.








shape.h


#ifndef SHAPE_H


#define SHAPE_H


class Shape


{


     public:


           virtual double area()
const {return
0.0;}


           virtual double
volume() const {return
0.0;}


           virtual void
printShapeName() const = 0; // tiszta


           virtual void print() const = 0;          // tiszta


};


#endif





point.h


#ifndef POINT_H


#define POINT_H


#include <iostream>


using std::cout;


#include "shape.h"


class Point : public
Shape


{


     public:


           Point(int = 0, int = 0);


           void setPoint(int, int);


           virtual void
printShapeName() const {cout << "Point: ";}


           virtual void print() const;


     private:


           int x, y; // a pont
koordinátái


};


#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;


}





void Point::print() const{


     cout
<< '[' << x << ", " << y << ']';


}





circle.h


#ifndef CIRCLE_H


#define CIRCLE_H


#include "point.cpp"


class Circle : public
Point


{


     public:


           Circle(double r = 0.0, int x
= 0, int y = 0);


           void setRadius(double);


           virtual double area()
const;


           virtual void
printShapeName() const {cout << "Circle: ";}


           virtual void print() const;


     protected:


           double radius;


};


#endif





circle.cpp


#include <iostream>


using std::cout;


#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::area() const{


     return 3.14159 * radius * radius;


}





void Circle::print() const


{


     Point::print();


     cout
<< "; Sugár = " <<
radius;


}





cylinder.h


#ifndef CYLINDER_H


#define CYLINDER_H


#include "circle.h"


class Cylinder : public
Circle


{


     public:


           Cylinder(double h = 0.0, double
r = 0.0,


           int x = 0, int y =
0);


           void setHeight(double);


           virtual double area()
const;


           virtual double
volume() const;


           virtual void
printShapeName() const {cout << "Cylinder: ";}


           virtual void print() const;


     private:


           double height;


};


#endif





cylinder.cpp


#include <iostream>


using std::cout;


#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::area() const{


     return 2 * Circle::area() + 2 * 3.14159 * radius *
height;


}





double Cylinder::volume() const{


     return Circle::area() * height;


}





void Cylinder::print() const


{


     Circle::print();


     cout
<< "; Magasság = " <<
height;


}





test_shape.cpp


#include<iostream>


using std::cout;


using std::endl;


#include <iomanip>


using std::ios;


using std::setiosflags;


using std::setprecision;


#include "cylinder.h"





void virtualViaPointer(const
Shape*);


void virtualViaReference(const
Shape&);





int main()


{


     cout
<< setiosflags(ios::fixed | ios::showpoint) << setprecision(2); //double-nek


     Point
point(7, 11);                 // x = 7, y = 11


     Circle
circle(3.5, 22, 8);          // radius = 3.5,
x = 22, y = 8


     Cylinder
cylinder(10, 3.3, 10, 10); // height = 10, radius =
3.3, x = 10, y = 10





     point.printShapeName();
//static binding


     point.print();
         //kiírja a
koordinátákat


     cout
<< endl;





     circle.printShapeName();
//static binding


     circle.print();
         //kiírja a
koordinátákat + a sugarat


     cout
<< endl;





     cylinder.printShapeName();
//static binding


     cylinder.print();          //kiírja a
koordinátákat + a sugarat + a magassgot


     cout
<< '\n';





     Shape
*arrayOfShapes[3];     //3 elemű tömb, melyben Shape mutatók vannak


     arrayOfShapes[0]
= &point;   //Az
első a point objektumra mutat


     arrayOfShapes[1]
= &circle;  //A
második a circle objektumra mutat


     arrayOfShapes[2]
= &cylinder;//A harmadik a cylinder objektumra
mutat





     //Mindenik tagra meghívjuk a virtuális függvényeket kiíró függvényt


     cout
<< "\nA Shape virtuális függvényei
mutatókkal meghívva\n"
;


     for(int i = 0; i <
3; i++) virtualViaPointer(arrayOfShapes[i]);


          


     //Mindenik tagra meghívjuk a virtuális függvényeket kiíró függvényt


     cout
<< "A Shape virtuális függvényei
referenciákkal meghívva\n"
;


     for(int i = 0; i <
3; i++) virtualViaReference(*arrayOfShapes[i]);





     return 0;


}





//Meghívja a virtuális függvényeket
a mutató segítségével


void virtualViaPointer(const
Shape* baseClassPtr)


{


     baseClassPtr->printShapeName();
//dynamic binding


     baseClassPtr->print();          //dynamic binding


     cout
<< "\nTerület = " <<
baseClassPtr->area() << "\nTérfogat =
"
<< baseClassPtr->volume() << "\n\n";


}





//Meghívja a virtuális függvényeket
a referencia segítségével


void virtualViaReference(const
Shape& baseClassRef)


{


     baseClassRef.printShapeName();
//dynamic binding


     baseClassRef.print();          //dynamic
binding


     cout
<< "\nTerület = " <<
baseClassRef.area() << "\nTérfogat =
"
<< baseClassRef.volume() << "\n\n";


}





A kimenet:


Circle: [22, 8];
Sugár = 3.50


Terület = 38.48


Térfogat = 0.00





Cylinder: [10,
10]; Sugár = 3.30; Magasság = 10.00


Terület = 275.77


Térfogat =
342.12





A Shape
virtuális függvényei referenciákkal meghívva


Point: [7, 11]


Terület = 0.00


Térfogat = 0.00





Circle: [22, 8];
Sugár = 3.50


Terület = 38.48


Térfogat = 0.00





Cylinder: [10,
10]; Sugár = 3.30; Magasság = 10.00


Terület = 275.77


Térfogat =
342.12





A virtuális függvényeket meghívó virtualViaPointer és virtualViaReference függvények esetén a
fordító nem tudja meghatározni, hogy milyen típusú lesz a baseClassPtr objektum, ami akár problémák a forrása is lehet.






Find Out
Related Post



Ikuti AltairGate.com pada Aplikasi GOOGLE NEWS : FOLLOW (Dapatkan Berita Terupdate tentang Dunia Pendidikan dan Hiburan). Klik tanda  (bintang) pada aplikasi GOOGLE NEWS.

Top Post Ad

Below Post Ad

s