Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Билеты по ООП.doc
Скачиваний:
4
Добавлен:
14.04.2019
Размер:
596.48 Кб
Скачать

47. Идентификация типа во время исполнения (rtti)

Используя идентификацию во время исполнения можно определить тип объекта во время исполнения программы. Для этого используется функция typeid. Для использования этой функции необходимо подключить заголовочный файл typeinfo.h. Общая форма записи функции typeid следующая:

typeid(объект)

Здесь объект является объектом, чей тип требуется определить. Функция typeid возвращает ссылку на объект типа type_info, который описывает тип объекта объект. Класс type_info определяет следующие публичные члены:

bool operator==(const type_info &ob) const;

bool operator!= (const type_info &ob) const;

bool before(const type_info &ob) const;

const char *name() const;

Перегруженные операторы == и != обеспечивают сравнение типов. Функция before() возвращает истину, если вызываемый объект стоит выше в иерархии объектов, чем объект, используемый в качестве параметра. Функция name() возвращает указатель на имя типа.

Когда функция typeid применяется к указателю на базовый класс полиморфного типа, она автоматически возвращает тип объекта, на который указывает указатель, в том числе любой класс, выведенный из базового класса. (Как уже говорилось, полиморфным классом называется класс, содержащий хотя бы одну виртуальную функцию.)

Пример

#include <iostream>

#include <typeinfo.h>

using namespace std;

class BaseClass {

int a, b;

virtual void f(){}

};

class Derived1: public BaseClass {

void f(){cout << "In class Derived1" << endl;}

int i, j;

};

class Derived2: public BaseClass {

int k;

};

int main()

{

int i;

BaseClass *p, baseob;

Derived1 ob1;

Derived2 ob2;

// Сначала выводится имя встроенного типа

cout << "Typeid of i is ";

cout << typeid(i).name() << endl;

//

cout << "Exp" << endl;

cout << typeid(typeid(i)).name() << endl;

// демонстрация typeid с классами

p = &baseob;

cout << "p is pointed to an object of type ";

cout << typeid(*p).name() << endl;

p = &ob1;

cout << "p is pointed to an object of type ";

cout << typeid(*p).name() << endl;

p = &ob2;

cout << "p is pointed to an object of type ";

cout << typeid(*p).name() << endl;

if( typeid(baseob).before(typeid(ob1))) cout << "baseob before ob1" << endl;

else cout << "baseof not before ob1" << endl;

getchar();

return 0;

}

Программа выдаст следующий результат на экран

Typeid of i is int

Exp

class type_info

p is pointed to an object of type class BaseClass

p is pointed to an object of type class Derived1

p is pointed to an object of type class Derived2

baseob before ob1

RTTI не является тем средством, которое используется всякой программой. Однако, при работе с полиморфными типами она позволяет знать тип объекта в любой ситуации.

49. Виртуальный базовый класс

В С++ ключевое слово virtual используется для объявления виртуальных функций, которые будут переопределены в производных классах. Однако ключевое слово virtual также имеет другое использование, позволяющее определить виртуальный базовый класс.

Пример некорректной программы.

Программа содержит ошибку и не будет компилироваться.

#include <iostream>

using namespace std;

class base {

public:

int i;

};

class d1 : public base {

public:

int j;

};

class d2 : public base {

public:

int k;

};

class d3 : public d1, public d2 {

public:

int m;

};

int main()

{

d3 d;

d.i = 10;

d.j = 20;

d.k = 30;

d.m = 40;

cout << d.i << " ";

cout << d.j << " " << d.k << " ";

cout << d.m;

getchar();

return 0;

}

Имеются два способа исправить программу. Первый заключается в использовании оператора области видимости для переменной i с дальнейшим выбором вручную одного из i.

#include <iostream>

using namespace std;

class base {

public:

int i;

};

class d1 : public base {

public:

int j;

};

class d2 : public base {

public:

int k;

};

class d3 : public d1, public d2 {

public:

int m;

};

int main()

{

d3 d;

d.d2::i = 10;

d.j = 20;

d.k = 30;

d.m = 40;

cout << d.d2::i << " ";

cout << d.j << " " << d.k << " ";

cout << d.m;

getchar();

return 0;

}

Как можно видеть, используя оператор области видимости, в программе вручную выбирается версия d2 класса base. Что если требуется только одна копия класса base? Решение достигается путём использования виртуального базового класса.

Пример

#include <iostream>

using namespace std;

class base {

public:

int i;

};

class d1 : virtual public base {

public:

int j;

};

class d2 : virtual public base {

public:

int k;

};

class d3 : public d1, public d2 {

public:

int m;

};

int main()

{

d3 d;

d.i = 10;

d.j = 20;

d.k = 30;

d.m = 40;

cout << d.i << " ";

cout << d.j << " " << d.k << " ";

cout << d.m;

getchar();

return 0;

}

Как видно, ключевое слово virtual предшествует спецификации наследуемого класса. Теперь оба класса d1 и d2 наследуют класс base как виртуальный. Любое множественное наследование с их участием порождает теперь включение только одной копии класса base. Поэтому в классе d3 имеется только одна копия base.

50. Операторы .* и ->*

Операторы .* и ->* называются операторами – указателями на член. Они позволяют указывать на член класса, а не на конкретный экземпляр члена в каком-то объекте. Эти два оператора нужны поскольку указатель на член класса не определяет полностью адрес. Вместо этого он обеспечивает смещение до члена в классе. Поскольку указатели на член не являются указателями в полном смысле этого слова, то обычные операторы . и -> не могут использоваться.

Пример

#include <iostream>

using namespace std;

class myclass{

public:

int sum;

int mum;

void sum_it(int x);

};

void myclass::sum_it(int x){

int i;

sum = 0;

for(i=x; i; i--) sum+=i;

}

int main()

{

int myclass::*dp;

void (myclass::*fp)(int x);

myclass c;

dp = &myclass::sum;

fp = &myclass::sum_it;

(c.*fp)(7);

cout << "Summation of 7 is " << c.*dp;

return 0;

}

Внутри функции main() программа создаёт два указателя на член: dp, указывающий на переменную типа int, и fp, указывающий на функцию, ничего не возвращающую и имеющую аргумент типа int. Для указания класса используется оператор области видимости. В программе также создаётся объект с класса myclass.

При осуществлении доступа к члену объекта с использованием самого объекта или ссылки на него необходимо применять оператор .*, однако, если используется указатель на объект, то потребуется оператор ->*, как в следующем примере.

#include <iostream>

using namespace std;

class myclass{

public:

int sum;

int mum;

void sum_it(int x);

};

void myclass::sum_it(int x){

sum = (x*(x+1))/2;

}

int main()

{

int myclass::*dp;

void (myclass::*fp)(int x);

myclass *c, d;

c = &d;

dp = &myclass::sum;

fp = &myclass::sum_it;

(c->*fp)(7);

cout << "Summation of 7 is " << c->*dp;

return 0;

}

В этой версии с является указателем на объект типа myclass, а оператор ->* используется для доступа к членам sum и sum_it().

51-52. Операторы const_cast и dynamic_cast. Операторы reinterpret_cast и static_cast.

Хотя С++ полностью поддерживает традиционное приведение типов языка С, для него определено четыре дополнительных оператора приведения типов. Ими являются: const_cast, dynamic_cast, reinterpret_cast и static_cast. Общая форма записи имеет следующий вид:

const_cast<тип>(объект)

dynamic_cast<тип>(объект)

reinterpret_cast<тип>(объект)

static_cast<тип>(объект)

Здесь тип определяет целевой тип, а объект является объектом, приводимым к новому типу.

Оператор const_cast используется для явного переопределения const или volatile в операции приведения типа. Целевой тип должен быть тем же самым, что и исходный тип, за исключением его атрибута const или volatile. Наиболее частым употреблением const_cast является удаление атрибута const.

Оператор dynamic_cast выполняет приведение типов во время исполнения с проверкой возможности приведения типа. Если такое приведение не может быть сделано, то выражение принимает значение NULL. Его основным применение служит приведение полиморфных типов. Например, пусть имеются два полиморфных класса B и D, где D выведено из B. Оператор dynamic_cast может привести указатель D* к указателю B*. Оператор dynamic_cast может привести указатель B* к указателю D* только в том случае, если указатель фактически указывает на объект D.

Пример

#include <iostream>

#include <vector>

using namespace std;

#define NUM 4

class employee {

public:

employee() {cout << "Constructing employee\n";}

virtual void print() = 0;

};

class programmer : public employee {

public:

programmer() {cout << "Constructing programmer\n";}

void print() {cout << "Printing programmer\n";}

};

class salesperson : public employee {

public:

salesperson(){cout << "Constructing salesperson\n";}

void print() {cout << "Printing salesperson\n";}

};

class executive : public employee {

public:

executive(){cout << "Constructing executive\n";}

void print() {cout << "Printing executive\n";}

};

int main()

{

programmer prog1, prog2;

executive ex;

salesperson sp;

vector <employee*> e(NUM);

e[0] = &prog1;

e[1] = &sp;

e[2] = &ex;

e[3] = &prog2;

for(int i = 0; i < NUM; i++) {

programmer *pp = NULL;

pp=dynamic_cast<programmer*>(e[i]);

if(pp) {

cout << "Is a programmer\n";

pp->print();

}

else {

cout << "Not a programmer\n";

}

}

getchar();

return 0;

}

Массив содержит указатели на четырёх работников. Оператор dynamic_cast используется для идентификации того, кто из них является программистом. Если dynamic_cast возвращает NULL, то работник не является программистом. В противном случае вызывается функция print().

Программа выдаст следующий результат на консоль:

Constructing employee

Constructing programmer

Constructing employee

Constructing programmer

Constructing employee

Constructing executive

Constructing employee

Constructing salesperson

Is a programmer

Printing programmer

Not a programmer

Not a programmer

Is a programmer

Printing programmer

Оператор static_cast выполняет не полиморфное приведение типов. Например, он может использоваться для приведения указателя на базовый класс к указателю на производный класс. Также может использоваться для любого стандартного указателя. Никаких проверок времени исполнения не выполняется.

Оператор reinterpret_cast изменяет один тип в фундаментально иной тип. Например, он может быть использован для преобразования указателя в целое число. Оператор reinterpret_cast предназначен для использования при приведении типов указателей, несопоставимых в отношении наследования.

Пример

#include <iostream>

using namespace std;

int main()

{

int i=0;

const char *p = "This is a string";

i = reinterpret_cast<int> (p);

cout << i<<endl;

getchar();

return 0;

}

Хотя новые операторы присвоения типов не требуются для простых программ, они могут оказаться полезными в более сложных приложениях С++.

Cтатические члены класса

Члены класса могут использоваться с ключевым словом static. Когда член класса объявляется как статический, то компилятору даётся указание, что должна существовать только одна копия этого члена, сколько бы объектов класса не создавалось. Статический член используется совместно всеми объектами данного класса. Все статические данные инициализируются нулями при создании первого объекта, другая инициализация не предусмотрена.

При объявлении статического члена данных класса этот член не определяется. Вместо этого необходимо обеспечить для него глобальное определение вне класса.

Пример

#include <iostream>

using namespace std;

class counter{

static int count;

public:

void setcount(int i){ count = i; }

void showcount() { cout << count << " ";}

};

int counter::count;

int main()

{

counter a, b;

a.showcount();

b.showcount();

a.setcount(10);

a.showcount();

b.showcount();

getchar();

return 0;

}

Программа выдаст

0 0 10 10