9.Информация за типа по време на изпълнение. Булев тип.
RTTI
Информацията за типа по време на изпълнение( Runtime type information ) представлява средства, чрез които може да се установи типа на даден обект по време на изпълнение на програмта. Ще разгледаме 2 важни операции RTTI – typeid и dynamic_cast. За използване на RTTI някои компилатори изискват включване на RTTI възможности.
typeid
Използването на typeid изисква включване на заглавен файл typeinfo. Ще съставим шаблон на функцията maximum с три параметъра от тип Т, като тя определя и връща най-големия сред тях.
#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 > max ) max = value2; if( value3 > max ) max = value3; const char *dataType = typeid(T).name(); cout << dataType << " maximum" << endl; return max; }
Функцията-елемент name() връща определения от реализацията низ, който е името на типа Т. Това може, а може и да не е пригодено за четене от хора.
void main() { int a = 8, b = 88, c = 22; double d = 95.96, e = 78.59, f = 83.89; cout << maximum( a, b, c ) << endl; cout << maximum( d, e, f ) << endl; }
Операцията typeid връща псевдоним на обекта typeinfo – това е поддържан от системата обект, който представлява типа и не трябва да се унищожава с delete. Използването на typeid в switch - подобни оператори е недопустимо в RTTI(стойностите в switch трябва да са известни по време на компилация, а RTTI е по време на изпълнение). Вместо това трябва да се използват виртуални функции.
dynamic_cast
Операцията dynamic_cast осигурява извършването на правилни преобразувания по време на изпълнение(защото по време на компилация не може да се провери дали преобразуванието е допустимо). Операцията dynamic_cast често се използва за понижаващо преобразувание на указател от базовия клас в указател от производен клас. Следната програма, демонстрира изпозлването на dynamic_cast.
#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 * height + 2 * Circle::area(); } private: int height; };
Съдържащ виртуална функция area, производният клас Circle открито наследява Shape, а производният клас Cylinder открито наследява Circle. Двата производни класа предефинират операцията area.
Забележка: Това не е хубаво ООП! В истинска програма класът Цилиндър никога няма да наследява класа Кръг, заради същата причина, поради която Стая не наследява Стена. Това е глупав пример, като оправданието за него е, че е… просто пример.
void outputShapeArea( const Shape* ); void main() { Circle circle; Cylinder cylinder; Shape *ptr = 0; outputShapeArea( &circle ); outputShapeArea( &cylinder ); outputShapeArea( ptr ); }
Всяко извикване дава един отъ трите резултата – лице на кръг, пълна повърхнина на цилиндър или съобщение, че фигурата за Shape не е нито кръг нито цилиндър. Функцията outputShapeArea приема указател към Shape като параметър. При първото извикване получава адреса на обекта circle, при второто адреса на обекта cylinder, и третото получава указател ptr към базовия клас.
void outputShapeArea( const Shape* shapePtr ) { const Circle *circlePtr; const Cylinder *cylinderPtr; cylinderPtr = dynamic_cast< const Cylinder* > ( shapePtr ); if ( cylinderPtr != 0 ) cout << “ Пълната повърхнина на цилиндъра е ” << shapePtr->area(); else { circlePtr = dynamic_cast< const Circle* > ( shapePtr ); if ( circlePtr != 0 ) cout << “ Лицето на кръга: ” << shapePtr->area(); else cout << “ Нито кръг, нито цилиндър.. ”<< endl; } }
Забележка: Това също не е хубаво ООП! Като основно правило, ако в някой момент се наложи класове да бъдат изброявани, значи нещо не е както трябва. На работа така не се прави! Нека не му обърщаме внимание и да продължаваме напред.
Отначало се извършва динамичното преобразуване на типа на shapePtr от const Shape* в const Cylinder* с изпозлването на dynamic_cast. В резултат на указателя на cylinderPtr се присвоява адреса на обекта cylinder или 0(NULL) за означение на това, че формата не е cylinder. Ако резултатът от преобразуването не е 0, се извежда пълната повърхнина на цилиндъра. След това се извършва динамично преобразуване на типа на shapePtr от const Shape* в const Circle* с използване на dynamic_cast. В резултат на указателя на circlePtr се орисвоява адреса на обекта circle или 0, ако формата не е кръг. Ако резултатът не е 0, се извежда лицето на кръга. При опит да се използва dynamic_cast за указатели от тип void* се получава съобщение за синтактична грешка. RTTI е предназначена за използване в йерархия на полиморфно наследяване с виртуални функции.
Булев тип.
Булевият тип bool е едно от допълненията към стандарта на С++. Променлива от тип Bool има само две състояния - true и false - тя се състои само от един бит. За нея са дефинирани побитовите операции, както и стандартните за C++ оператори. Например:
bool bool1 = false, bool2 = true; cout << bool1 << endl << bool2 << endl; /* Ще се изведат 0 и 1 по премълчаване. За да се изведат true, false, трябва да бъде използван манипулаторът boolalpha. */ cout << boolalpha << bool1 << endl << bool2 << endl;
Указатели и променливи от тип int, double и т.н. може неявно да бъдат преобразувани към bool. По стандарт в C++ Нулевите стойности се преобразуват към false, а ненулевите към true. Операторът отрицание е удобен за целта - !a връща 1, ако a е 0, и 0 иначе. Поради това, двойното му прилагане може да се използва при преобразуване към bool:
int a = 5, b = 0; cout << !a << endl << !b << endl; // Ще отпечата 0 1 cout << !!a << endl << !!b << endl; // Ще отпечата 1 0
dynamic_cast няма нищо общо с bool, но явно преподавателят е решил да се бори за по-голям обем в този въпрос :)





