Информация за типа по време на изпълнение. Булев тип.

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, но явно преподавателят е решил да се бори за по-голям обем в този въпрос :)

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License