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


 Расширенная динамическая информация о типе



Pdf көрінісі
бет246/256
Дата11.07.2022
өлшемі2,87 Mb.
#37591
1   ...   242   243   244   245   246   247   248   249   ...   256
13.5.4 Расширенная динамическая информация о типе 
В классе Type_info содержится только минимум информации, необходимой для идентификации типа и 
безопасных операций приведения. Но поскольку в самом классе Type_info есть функции-члены info() и 
get_info(), можно построить производные от него классы, чтобы в динамике определять, какие объекты 
Type_info возвращают эти функции. Таким образом, не меняя класса Type_info, пользователь может 
получать больше информации о типе с помощью объектов, возвращаемых функциями dynamic_type() и 
static_type(). Во многих случаях дополнительная информация должна содержать таблицу членов 
объекта: 
struct Member_info { 
char* 
name; 
Type_info* 
tp; 
int 
offset; 
}; 
class Map_info : public Type_info { 
Member_info** 
mi; 
public: 
static const Type_info info_obj; 
virtual typeid get_info() const; 
static 
typeid 
info(); 
// функции доступа 
}; 
Класс Type_info вполне подходит для стандартной библиотеки. Это базовый класс с минимумом 
необходимой информации, из которого можно получать производные классы, предоставляющие 
больше информации. Эти производные классы могут определять или сами пользователи, или какие-то 
служебные программы, работающие с текстом на С++, или сами трансляторы языка. 
13.5.5 Правильное и неправильное использование динамической
информации о типе 
Динамическая информация о типе может использоваться во многих ситуациях, в том числе для: 
объектного ввода-вывода, объектно-ориентированных баз данных, отладки. В тоже время велика 
вероятность ошибочного использования такой информации. Известно,что в языке Симула 
использование таких средств, как правило, приводит к ошибкам. Поэтому эти средства не были 
включены в С++. Слишком велик соблазн воспользоваться динамической информацией о типе, тогда 
как правильнее вызвать виртуальную функцию. Рассмотрим в качестве примера класс Shape из $$1.2.5. 
Функцию rotate можно было задать так
void rotate(const Shape& s) 
// неправильное использование динамической 
// информации о типе 

if (ref_type_info(s)==static_type_info(Circle)) { 
/
/ для этой фигуры ничего не надо 

else if (ref_type_info(s)==static_type_info(Triangle)) { 
// 
вращение треугольника 

else if (ref_type_info(s)==static_type_info(Square)) { 
// вращение квадрата 

// 
... 

Если для переключателя по типу поля мы используем динамическую информацию о типе, то тем самым 
нарушаем в программе принцип модульности и отрицаем сами цели объектно-ориентированного 
программирования. К тому же это решение чревато ошибками: если в качестве параметра функции 
будет передан объект производного от Circle класса, то она сработает неверно (действительно, 


Бьерн Страуструп.
Язык программирования С++ 
 
351 
вращать круг (Circle) нет смысла, но для объекта, представляющего производный класс, это может 
потребоваться). Опыт показывает, что программистам, воспитанным на таких языках как С или Паскаль, 
трудно избежать этой ловушки. Стиль программирования этих языков требует меньше 
предусмотрительности, а при создании библиотеки такой стиль можно просто считать небрежностью. 
Может возникнуть вопрос, почему в интерфейс с системой динамической информации о типе включена 
условная операция приведения ptr_cast(), а не операция is_base(), которая непосредственно 
определяется с помощью операции has_base() из класса Type_info. Рассмотрим такой пример: 
void f(dialog_box& db) 

if (is_base(&db,dbox_w_str)) { 
// 
является ли db базовым 
// 
для dbox_w-str? 
dbox_w_str* dbws = (dbox_w_str*) &db; 
// 
... 

// 
... 

Решение с помощью ptr_cast ($$13.5) более короткое, к тому же здесь явная и безусловная операция 
приведения отделена от проверки в операторе if, значит появляется возможность ошибки, 
неэффективности и даже неверного результата. Неверный результат может возникнуть в тех редких 
случаях, когда система динамической идентификации типа распознает, что один тип является 
производным от другого, но транслятору этот факт неизвестен, например: 
class D; 
class B; 
void g(B* pb) 

if (is_base(pb,D)) { 
D* pb = (D*)pb; 
// 
... 

// 
... 

Если транслятору пока неизвестно следующее описание класса D: 
class D : public A, public B { 
// 
... 
}; 
то возникает ошибка, т.к. правильное приведение указателя pb к D* требует изменения значения 
указателя. Решение с операцией ptr_cast() не сталкивается с этой трудностью, поскольку эта операция 
применима только при условии, что в области видимости находятся описания обеих ее параметров. 
Приведенный пример показывает, что операция приведения для неописанных классов по сути своей 
ненадежна, но запрещение ее существенно ухудшает совместимость с языком С. 


Достарыңызбен бөлісу:
1   ...   242   243   244   245   246   247   248   249   ...   256




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

    Басты бет