Бьерн Страуструп. Язык программирования С++



бет119/124
Дата16.07.2016
өлшемі3.27 Mb.
#204081
түріКнига
1   ...   116   117   118   119   120   121   122   123   124

13.5.2 Класс Type_info


В классе Type_info есть минимальный объем информации для реализации

операции ptr_cast(); его можно определить следующим образом:
class Type_info {

const char* n; // имя

const Type_info** b; // список базовых классов

public:


Type_info(const char* name, const Type_info* base[]);
const char* name() const;

Base_iterator bases(int direct=0) const;

int same(const Type_info* p) const;

int has_base(const Type_info*, int direct=0) const;

int can_cast(const Type_info* p) const;
static const Type_info info_obj;

virtual typeid get_info() const;

static typeid info();

};
Две последние функции должны быть определены в каждом производном

от Type_info классе.

Пользователь не должен заботиться о структуре объекта Type_info, и

она приведена здесь только для полноты изложения. Строка, содержащая

имя типа, введена для того, чтобы дать возможность поиска информации

в таблицах имен, например, в таблице отладчика. С помощью нее а также

информации из объекта Type_info можно выдавать более осмысленные

диагностические сообщения. Кроме того, если возникнет потребность

иметь несколько объектов типа Type_info, то имя может служить уникальным

ключом этих объектов.
const char* Type_info::name() const

{

return n;



}
int Type_info::same(const Type_info* p) const

{

return this==p || strcmp(n,p->n)==0;



}
int Type_info::can_cast(const Type_info* p) const

{

return same(p) || p->has_base(this);



}
Доступ к информации о базовых классах обеспечивается функциями

bases() и has_base(). Функция bases() возвращает итератор, который

порождает указатели на базовые классы объектов Type_info, а с

помощью функции has_base() можно определить является ли заданный класс

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

direct, который показывает, следует ли рассматривать все базовые классы

(direct=0), или только прямые базовые классы (direct=1). Наконец,

как описано ниже, с помощью функций get_info() и info() можно

получить динамическую информацию о типе для самого класса Type_info.

Здесь средство динамических запросов о типе сознательно

реализуется с помощью совсем простых классов. Так можно избежать

привязки к определенной библиотеке. Реализация в расчете на

конкретную библиотеку может быть иной. Можно, как всегда, посоветовать

пользователям избегать излишней зависимости от деталей реализации.

Функция has_base() ищет базовые классы с помощью имеющегося в

Type_info списка базовых классов. Хранить информацию о том, является

ли базовый класс частным или виртуальным, не нужно, поскольку

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

будут выявлены при трансляции.
class base_iterator {

short i;


short alloc;

const Type_info* b;

public:

const Type_info* operator() ();



void reset() { i = 0; }
base_iterator(const Type_info* bb, int direct=0);

~base_iterator() { if (alloc) delete[] (Type_info*)b; }

};
В следующем примере используется необязательный параметр для указания,

следует ли рассматривать все базовые классы (direct==0) или только прямые

базовые классы (direct==1).
base_iterator::base_iterator(const Type_info* bb, int direct)

{

i = 0;


if (direct) { // использование списка прямых базовых классов

b = bb;


alloc = 0;

return;


}
// создание списка прямых базовых классов:
// int n = число базовых

b = new const Type_info*[n+1];

// занести базовые классы в b
alloc = 1;

return;


}
const Type_info* base_iterator::operator() ()

{

const Type_info* p = &b[i];



if (p) i++;

return p;

}
Теперь можно задать операции запросов о типе с помощью макроопределений:
#define static_type_info(T) T::info()
#define ptr_type_info(p) ((p)->get_info())

#define ref_type_info(r) ((r).get_info())


#define ptr_cast(T,p) \

(T::info()->can_cast((p)->get_info()) ? (T*)(p) : 0)

#define ref_cast(T,r) \

(T::info()->can_cast((r).get_info()) \

? 0 : throw Bad_cast(T::info()->name()), (T&)(r))
Предполагается, что тип особой ситуации Bad_cast (Ошибка_приведения)

описан так:


class Bad_cast {

const char* tn;

// ...

public:


Bad_cast(const char* p) : tn(p) { }

const char* cast_to() { return tn; }

// ...

};
В разделе $$4.7 было сказано, что появление макроопределений



служит сигналом возникших проблем. Здесь проблема в том, что только

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

а макроопределения скрывают специфику реализации. По сути для хранения

информации для динамических запросов о типах предназначена таблица

виртуальных функций. Если реализация непосредственно поддерживает

динамическую идентификацию типа, то рассматриваемые операции можно

реализовать более естественно, эффективно и элегантно. В частности,

очень просто реализовать функцию ptr_cast(), которая преобразует

указатель на виртуальный базовый класс в указатель на его производные

классы.


13.5.3 Как создать систему динамических запросов о типе


Здесь показано, как можно прямо реализовать динамические запросы

о типе, когда в трансляторе таких возможностей нет. Это достаточно

утомительная задача и можно пропустить этот раздел, так как в нем

есть только детали конкретного решения.

Классы set и slist_set из $$13.3 следует изменить так, чтобы

с ними могли работать операции запросов о типе. Прежде всего, в

базовый класс set нужно ввести функции-члены, которые используют

операции запросов о типе:
class set {

public:


static const Type_info info_obj;

virtual typeid get_info() const;

static typeid info();
// ...

};
При выполнении программы единственным представителем объекта типа

set является set::info_obj, который определяется так:
const Type_info set::info_obj("set",0);
С учетом этого определения функции тривиальны:
typeid set::get_info() const { return &info_obj; }

typeid set::info() { return &info_obj; }

typeid slist_set::get_info() const { return &info_obj; }

typeid slist_set::info() { return &info_obj; }


Виртуальная функция get_info() будет предоставлять операции

ref_type_info() и ptr_type_info(), а статическая функция info()

- операцию static_type_info().

При таком построении системы запросов о типе основная трудность

на практике состоит в том, чтобы для каждого класса объект типа

Type_info и две функции, возвращающие указатель на этот объект,

определялись только один раз.

Нужно несколько изменить класс slist_set:


class slist_set : public set, private slist {

// ...


public:

static const Type_info info_obj;

virtual typeid get_info() const;

static typeid info();


// ...

};
static const Type_info* slist_set_b[]

= { &set::info_obj, &slist::info_obj, 0 };

const Type_info slist_set::info_obj("slist_set",slist_set_b);


typeid slist_set::get_info() const { return &info_obj; }

typeid slist_set::info() { return &info_obj; }





Достарыңызбен бөлісу:
1   ...   116   117   118   119   120   121   122   123   124




©dereksiz.org 2024
әкімшілігінің қараңыз

    Басты бет