(toc)
Az adat típusának átalakítását egy másik típusra type-cast-nak nevezik. Két típusú átalakítás
lehetséges: az implicit és explicit átalakítás. Az implicit átalakításhoz nincs
szükség cast-re, mert az
átalakítás automatikusan megtörténik a másolásnál ha a cél memóriahelye
kompatibilis adattípust tárol.
short a=2000;
int b;
b=a;
Ebben a példában a short típusú változó int típussú változóvá alakult. A hasonló átalakítások
néha veszítenek az adatok pontosságából, de ezt a fordító jelzi. Nem csak az
alapvető adattípusokat lehet átalakítani, hanem az osztály típusú adatokat is.
class A {};
class B {
public: B (A a) {} };
A a;
B b=a;
A B osztály konstruktora egyetlen A típusú objektumot használ paraméternek, így B objektuma hasonló lesz A objektumával és az átalakítás lehetséges.
Az eltérő értelmezést igénylő átalakítások
explicit átalakítások kell legyenek és ezek kétféleképp is meghatározhatóak:
short a=2000;
int b;
b = (int) a;
b = int (a);
Ezt e két operátor bármilyen átalakításnál
használható, ide értve az osztály-osztály és mutató-osztály átalakítást. Habár
egy ilyen átalakítás szintaktikailag helyes, mégis hibát vagy redundáns
kimenetet eredményezhet.
#include<iostream>
using namespace std;
class A {
float i,j;
};
class B {
int x,y;
public:
B
(int a, int b)
{ x=a; y=b; }
int result() { return
x+y;}
};
int main () {
A d;
B *
padd; //
padd egy B típusú mutató
padd
= (B*) &d; // padd az A típusú objektumra (d) mutat
cout
<< padd->result(); // az A osztálynak nincs
is result() függvénye
// mégsem jelez hibát a fordító
return 0;
}
A fenti programban A és B
osztály nem kompatibilisek egymással. Ennek ellenére az A objektumát (d) át lehet konvertálni, hogy a B mutatója (padd) rá mutasson. A padd használhatatlanná válik, és a fordító ezt
nincs honnan lássa.
A static_cast operátor
A standard C++ alapvetően négy cast operátort tartalmaz: static_cast, const_cast, reinterpret_cast és dynamic_cast. Az újabbak kevésbé használatosak, már csak azért
is hogy nincsenek ennyire általánosítva. Az implicit átalakításhoz hasonlóan
itt sem lehet szintaxis hibát észlelni egy hibás átalakításnál, viszont a
fordítás során minden adattípus ellenőrzésre kerül. A static_cast operátor a
standard adattípusok közti átalakításra jó, mint például void* -> char* vagy int -> double és ezek fordítottjai.
#include <iostream>
using std::cout;
using std::endl;
class BaseClass
{
public:
void f() const { cout
<< "Ősosztály\n"; }
};
class DerivedClass : public
BaseClass
{
public:
void f() const { cout
<< "Derivált osztály\n"; }
};
void test(BaseClass*);
int main()
{
double d = 8.22;
int x = static_cast<int>(d); // d átalakítása int-re
cout
<< "d = " << d
<< "\nx = " << x
<< endl;
BaseClass
*basePtr = new DerivedClass; //memóriafoglalás
test(basePtr);
//egy globális függvény
delete basePtr; //memória
felszabadítás
return 0;
}
void test(BaseClass *basePtr)
{
DerivedClass
*derivedPtr; //derivalt osztály típusó mutató
//az ősosztály típusú mutatót átalakítjuk derivált típusú mutatóvá
derivedPtr
= static_cast<DerivedClass*>(basePtr);
derivedPtr->f();
//az f a derivált osztályból fog lefutni
}
A kimenet:
d = 8.22
x = 8
Derivált osztály
A BaseClass *basePtr = new
DerivedClass utasítás
létrehoz egy BaseClass típusú mutatót és rögtön rá is irányítja egy DerivedClass típusú objektumra. Ezután a mutató mint
paraméter jut el a test
függvénybe, ahol értékét leadja egy DerivedClass típusú mutatónak. Ehhez a static_cast segítségével ő maga is át kell alakuljon DerivedClass típussá. Ezt a műveletet downcast-nak nevezik, mert felső osztálytípusból
alsó osztálytípusba történt az átalakítást. Ez általában veszélyes művelet,
mert a programozónak előbb biztosra kell tudnia, hogy az objektumok
kompatibilisek egymással, ebben az esetben például mindkét osztálynak
rendelkeznie kell az f()
függvénnyel.
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.
A const_cast operátor
A const és volatile típusu adatok átalakítására jó. A
következő programban a const_cast egy osztály tagváltozóját alakítja át, egy const tagfüggvény révén, amely normális esetben
nem változtathatná meg az objektum felépítését.
#include<iostream>
using std::cout;
class ConstCastTest
{
public:
void setNumber(int);
int getNumber() const;
void printNumber() const;
private:
int number;
};
void ConstCastTest::setNumber(int num) { number = num; }
int ConstCastTest::getNumber() const { return
number; }
void ConstCastTest::printNumber() const
{
cout
<< "\nprintNumber() = ";
//a number módosítás hibát adna, mert a függvény const
const_cast<ConstCastTest*>(this)->number--;
cout
<< number;
}
int main()
{
ConstCastTest
x;
x.setNumber(8);
cout
<< "getNumber() = " <<
x.getNumber();
x.printNumber();
return 0;
}
A kimenet:
getNumber() = 8
printNumber() = 7
A ConstCastTest osztály három publikus tagfüggvényt és egy privát tagváltozót tartalmaz.
Két tagfüggvény konstans, ami azt jelenti, hogy nem változtathat semmit a
tagváltozón. A printNumber függvényben a const_cast<ConstCastTest*>(this)->number-- utasítás ezt mégis lehetővé teszi. A this egy ConstCastTest típusú konstans mutató, mely konstans mivoltát a const_cast ideiglenesen feloldja és ezért módosítani
lehet a number értékét. A fordító
nem jelez hibát. A const_cast operátort nem lehet közvetlenül konstans változón használni, hanem
konstans mutatókkal vagy konstans referenciákkal működik, például:
int i = 8;
const int& ref =
i;
const_cast<int&>(ref)=7;
cout << "\ni = " << i;
A const_cast operátor konstans paraméter átadására is
használható nem-konstans paramétert váró függvénynek.
#include <iostream>
using namespace std;
void print (char *
str){ cout << str << endl;}
int main ()
{
const char * c = "Konstans sor";
( const_cast<char
*> (c) );
return 0;
}
A reinterpret_cast
operátor
A nem standard átalakítások a reinterpret_cast operátorral történnek. Inkább különböző típusú
mutatók között végez átalakítást, a standard típusokkal nem működik, mint a double -> int.
#include <iostream>
using std::cout;
#include <queue>
int main()
{
int x = 160;
int *ptr = &x;
cout
<< *reinterpret_cast<char*>(ptr);
return 0;
}
A kimenet:
á
A fenti programban a ptr mutató az x címével van inicializálva. Mindkettő int típus. A reinterpret_cast a mutatót char típussá alakítja. Ettől az x
értéke char-ként lesz értelmezve
és az eredmény az x
értékének ASCII kódja, ami éppen az á betű. Mivel a reinterpret_cast platform-függő, előfordulhat hogy egy-egy platformon másképp viselkedik.
Run-Time Type Information
(RTTI)
Az RTTI lehetővé teszi egy objektum típusának meghatározását a program
futása alatt. A következő példák a typeid és a dynamic_cast operátorokat mutatják be:
#include <iostream>
using std::cout;
using std::endl;
#include <typeinfo>
template <class T>
T maximum(T value1, T value2, T value3)
{
T max
= value1;
if(value2 > value1) max = value2;
if(value3 > max) max = value3;
const char* dataType
= typeid(T).name();
cout
<< "A(z) " << dataType
<< " típusú adatok közül a legnagyobb:
";
return max;
}
int main()
{
int a = 8, b = 88, c = 22;
double d = 95.96, e = 78.59, f = 83.99;
cout
<< maximum(a, b, c) << endl;
cout
<< maximum(d, e, f) << endl;
return 0;
}
A kimenet:
A(z) i típusú adatok közül a
legnagyobb: 88
A(z) d típusú adatok közül a
legnagyobb: 95.96
A typeid használatához szükséged alkalmazni a <typeinfo> headert. A const char* dataType
= typeid(T).name(); utasítás az, amelyik visszatéríti a T aktuális típusának nevét. A typeid egy operátor, amely egy referenciát
térít vissza egy type_info objektumról, amely egy adattípust képvisel. A typeid-t nem ajánlott switch-ben használni. Helyette inkább
használjunk virtuális
függvényeket.
A dynamic_cast operátor olyan típus átalakításokra jó, amely a
program során történik és a fordító nem észlelheti. Általában downcasting-re
használják a programozók, azaz amikor az alaposztály objektumát lekonvertálják
egy derivált osztály objektumává. Ezt csakis mutatókkal vagy referenciákkal
lehet használni. Az RTTI olyan öröklési hierarchiákban játszik szerepet, amely
rendelkezik a polimorfizmussal.
#include <iostream>
using std::cout;
using std::endl;
const double PI =
3.14159;
class Shape
{
public:
virtual double area()
const { return
0.0; }
};
class Circle : public
Shape
{
public:
Circle(int r = 1) { radius = r; }
virtual double area()
const { return
PI * radius * radius; }
protected:
int radius;
};
class Cylinder : public
Circle
{
public:
Cylinder(int h = 1) { height = h; }
virtual double area()
const { return
2 * PI * radius * height + 2 * Circle::area(); }
private:
int height;
};
void outputShapeArea(const
Shape*);
int main()
{
Circle
circle;
Cylinder
cylinder;
Shape
*ptr = 0;
outputShapeArea(&circle); //a kör területe
outputShapeArea(&cylinder); //a henger
területe
outputShapeArea(ptr);
//a
test területe
return 0;
}
void outputShapeArea(const
Shape* shapePtr)
{
const Circle *circlePtr;
const Cylinder *cylinderPtr;
cylinderPtr
= dynamic_cast<const
Cylinder*>(shapePtr); //Shape->Cylinder
if(cylinderPtr != 0) //ha át lehetett alakítani Cylinderbe
cout
<< "A henger területe: "
<< shapePtr->area();
else
{
circlePtr
= dynamic_cast<const
Circle*>(shapePtr); //Shape->Circle
if(circlePtr != 0) //ha át
lehetett alakítani Circlebe
cout
<< "A kör területe: "
<< circlePtr->area();
else
cout
<< "Nem kör és nem henger.";
}
cout
<< endl;
}
A kimenet:
A kör területe: 3.14159
A henger területe: 12.5664
Nem kör és nem henger.
A program a Shape ősosztályt használja, mely rendelkezik
egy virtuális függvénnyel, egy Circle
derivált osztállyal. A Circle-nek
is van egy derivált osztálya, a Cylinder. Mindkét derivált osztály felülírja az area virtuális függvényt. A main függvényben
deklarálva van egy-egy objektum a derivált osztályokból és egy mutató az
ősosztályból, amely nullával van inicializálva. Az outputShapeArea függvény paramétere is egy
ősosztály mutató, de ennek ellenére a main-ben derivált mutatókkal is meg van
hívva. Ezt a fordító nem veszi észre, ő csak azt látja, hogy a típus rendben
van. Ahhoz, hogy a program működjön is, a shapePtr mutatót downcasting-olni kell a derivált
mutatókra a dynamic_cast
segítségével. Ennek eredményeképp a cylinderPtr megkapja a shapePtr címét ha az const Cylinder* típus, különben zéró értéket vesz fel. Ha a kapott érték nem zéró, akkor
használható az area()
függvény. Ugyanez az eljárás a másik típuskonverzió esetében is.
Explicit konstruktorok
Az Overloading
című bejegyzésben bebizonyosodott, hogy bármilyen konstruktor, melynek van egy
paramétere, használható implicit átalakító operátorként. Például egy char-t átalakíthat osztály típussá. Az
átalakítás automatikusan történik, nem kell cast-okat használni. Bizonyos
esetekben azonban az ilyen művelet hibákat eredményezhet. Az alábbi programban
az Array osztály konstruktora
olyan objektumot alkot, mely egy egydimenziós tömböt állít elő a paraméternek
megadott elemszám alapján.
array.h
#ifndef ARRAY_H
#define ARRAY_H
#include <iostream>
using std::ostream;
class Array
{
friend ostream &operator<<(ostream&,
const Array&);
public:
Array(int = 10);
~Array();
private:
int size;
int *ptr;
};
#endif
array.cpp
#include <iostream>
using std::cout;
using std::ostream;
#include <cassert>
#include "array.h"
Array::Array(int
arraySize)
{
size
= (arraySize > 0 ? arraySize : 0);
cout
<< "Konstruktor: "<<
size;
ptr =
new int[size];
assert(ptr
!= 0);
for(int i = 0; i <
size; i++)
ptr[i]
= 0;
}
Array::~Array() { delete
[] ptr; }
ostream &operator<<(ostream
&output, const Array&a)
{
int i;
for(i = 0; i < a.size; i++)
output
<< a.ptr[i] << ' ';
return output;
}
test_array.cpp
#include <iostream>
using std::cout;
#include "array.h"
void outputArray(const
Array&);
int main()
{
Array
integers(7);
outputArray(integers);
outputArray(15);
return 0;
}
void outputArray(const
Array &arrayToOutput)
{
cout
<< "\nA tömb tartalma:\n"
<< arrayToOutput << "\n\n";
}
A kimenet:
Konstruktor: 7
A tömb tartalma:
0 0 0 0 0 0 0
Konstruktor: 15
A tömb tartalma:
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0
Az Array osztály int paraméterű
konstruktorát átalakító operátorként is lehet használni, int -> Array átalakításokra. A kiírásra z outputArray() függvényt használtuk, melynek paramétere const Array referencia. A főprogramban az integers egy Array típusú objektum, melynek deklarálásakor
azonnal lefut a konstruktor. Az outputArray(integers) utasítás csupán kiírja a tömb tartalmát a felhasználva a << operátort túlterhelő függvényt. Az outputArray(15) utasítás egész típussal hívja meg az outputArray() függvényt, ami nem int típusú paramétert vár, ezért átalakítja azt Array típussá. Ez egy olyan eset, amikor a
konstruktort átalakító operátorként használtuk, és ez sokszor bezavarhat a
program logikájába. A C++ az ilyen esetekre tartja fent az explicit kulcsszót, amelyet ha a konstruktor
prototípusa elé írunk az osztály definíciójában, akkor kizárja annak implicit
átalakító viselkedését. Ha a fenti programban átjavítjuk a definíciót explicit Array(int = 10)-re, akkor a fordító nem fogja engedni az outputArray(15) utasítás használatát. Helyette az outputArray(Array(15)) lesz a helyes.
Mutable tagváltozók
A mutable kulcsszó egy
alternatíva a const_cast-ra, mellyel
módosítani lehet a tagváltozókat a const tagfüggvényekben. Egy mutable tagváltozó változtatható marad a const tagfüggvényekben és const
objektumokban is. A const_cast és a mutable különböző
kontextusban teszik lehetővé a változtatást. Egy olyan const objektum esetén, melynek egyetlen mutable tagváltozója sincs, a const_cast operátort mindig alkalmazni kell
valahányszor változtatni szeretnénk egy tagváltozón. Ez a mechanizmus
jelentősen csökkenti az olyan tagváltozók véletlen módosítását, amelyek amúgy
módosíthatatlanok. Az olyan műveletek, amelyek igénybe veszik a const_cast operátort általában el vannak rejtve a
tagfüggvényekben.
#include <iostream>
using std::cout;
class TestMutable
{
public:
TestMutable(int v = 0) {value = v;}
void modifyValue() const
{value++;}
int getValue() const
{return value;}
private:
mutable int value;
};
int main()
{
const TestMutable t(99);
cout
<< "Kezdeti érték: "
<< t.getValue();
t.modifyValue();
//módosítja a mutable változót
cout
<< "\nMódosított érték: "
<< t.getValue();
return 0;
}
A kimenet:
Kezdeti érték: 99
Módosított érték: 100
A TestMutable osztály egy konstruktort, két const tagfüggvényt és egy mutable tagváltozót tartalmaz. A modifyValue tagfüggvény az amelyik módosítja a
tagváltozót. Normális esetben a const tagfüggvények nem kéne tudják módosítani
a tagváltozókat, hacsak az objektum ami felett dolgozik nincs a const_cast operátor hatása alatt. Mivel a value tagváltozó mutable, a const tagfüggvény módosítani tudja annak értékét 99-ről 100-ra.
Ikuti AltairGate.com pada Aplikasi GOOGLE NEWS : FOLLOW (Dapatkan Berita Terupdate tentang Dunia Pendidikan dan Hiburan). Klik tanda ☆ (bintang) pada aplikasi GOOGLE NEWS.