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


Динамическая информация о типе



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

13.5 Динамическая информация о типе


Иногда бывает полезно знать истинный тип объекта до его использования

в каких-либо операциях. Рассмотрим функцию my(set&) из $$13.3.
void my_set(set& s)

{

for ( T* p = s.first(); p; p = s.next()) {



// мой код

}

// ...



}
Она хороша в общем случае, но представим,- стало известно,

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

slist. Возможно также стал известен алгоритм перебора элементов, который

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

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

этот перебор является узким местом в системе. Тогда, конечно, имеет

смысл учесть в программе отдельно вариант с slist. Допустив возможность

определения истинного типа параметра, задающего множество, функцию

my(set&) можно записать так:
void my(set& s)

{

if (ref_type_info(s) == static_type_info(slist_set)) {



// сравнение двух представлений типа
// s типа slist
slist& sl = (slist&)s;

for (T* p = sl.first(); p; p = sl.next()) {


// эффективный вариант в расчете на list
}

}

else {


for ( T* p = s.first(); p; p = s.next()) {
// обычный вариант для произвольного множества
}

}

// ...



}
Как только стал известен конкретный тип slist, стали

доступны определенные операции со списками, и даже стала возможна

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

Приведенный вариант функции действует отлично, поскольку

slist - это конкретный класс, и действительно имеет смысл отдельно

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

теперь такую ситуацию, когда желательно отдельно разбирать вариант как

для класса, так и для всех его производных классов. Допустим, мы

имеем класс dialog_box из $$13.4 и хотим узнать, является ли он

классом dbox_w_str. Поскольку может существовать много производных

классов от dbox_w_str, простую проверку на совпадение с ним

нельзя считать хорошим решением. Действительно, производные классы

могут представлять самые разные варианты запроса строки. Например,

один производный от dbox_w_str класс может предлагать пользователю

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

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

от dbox_w_str классами. Это так же типично для узловых классов, как

проверка на вполне определенный тип типична для абстрактных классов,

реализуемых конкретными типами.
void f(dialog_box& db)

{

dbox_w_str* dbws = ptr_cast(dbox_w_str, &db);



if (dbws) { // dbox_w_str

// здесь можно использовать dbox_w_str::get_string()

}

else {
// ``обычный'' dialog_box



}
// ...

}
Здесь "операция" приведения ptr_cast() свой второй параметр

(указатель) приводит к своему первому параметру (типу) при условии, что

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

(или является производным классом от заданного типа). Для проверки

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

можно было сравнить с нулем.

Возможно альтернативное решение с помощью ссылки на dialog_box:


void g(dialog_box& db)

{

try {



dbox_w_str& dbws = ref_cast(dialog_box,db);
// здесь можно использовать dbox_w_str::get_string()
}

catch (Bad_cast) {


// ``обычный'' dialog_box
}
// ...

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

можно сравнивать, используется особая ситуация, обозначающая ошибку

приведения (т.е. случай, когда тип не есть dbox_w_str). Иногда

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

Различие функций ref_cast() и ptr_cast() служит хорошей

иллюстрацией различий между ссылками и указателями: ссылка обязательно

ссылается на объект, тогда как указатель может и не ссылаться,

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

13.5.1 Информация о типе


В С++ нет иного стандартного средства получения динамической информации

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

направлении.


Смоделировать такое средство довольно просто и в большинстве

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

Здесь предлагается решение, обладающее тем полезным свойством,

что объем информации о типе можно произвольно расширять. Его можно

реализовать с помощью вызовов виртуальных функций, и оно может

входить в расширенные реализации С++.

Достаточно удобный интерфейс с любым средством, поставляющим

информацию о типе, можно задать с помощью следующих операций:


typeid static_type_info(type) // получить typeid для имени типа

typeid ptr_type_info(pointer) // получить typeid для указателя

typeid ref_type_info(reference) // получить typeid для ссылки

pointer ptr_cast(type,pointer) // преобразование указателя

reference ref_cast(type,reference) // преобразование ссылки
Пользователь класса может обойтись этими операциями, а создатель

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

"приспособления", чтобы согласовать операции с реализацией

библиотеки.

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

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

ptr_cast() и ref_cast(). Таким образом пользователь отстраняется от

дальнейших сложностей, связанных с динамической идентификацией

типа. Кроме того, ограниченное использование динамической информации

о типе меньше всего чревато ошибками.

Если недостаточно знать, что операция приведения прошла успешно,

а нужен истинный тип (например, объектно-ориентированный

ввод-вывод), то можно использовать операции динамических запросов о типе:

static_type_info(), ptr_type_info() и ref_type_info(). Эти операции

возвращают объект класса typeid. Как было показано в примере с

set и slist_set, объекты класса typeid можно сравнивать. Для

большинства задач этих сведений о классе typeid достаточно. Но для

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

typeid есть функция get_type_info():
class typeid {

friend class Type_info;

private:

const Type_info* id;

public:

typeid(const Type_info* p) : id(p) { }



const Type_info* get_type_info() const { return id; }

int operator==(typeid i) const ;

};
Функция get_type_info() возвращает указатель на неменяющийся (const)

объект класса Type_info из typeid. Существенно, что объект

не меняется: это должно гарантировать, что динамическая информация

о типе отражает статические типы исходной программы. Плохо, если

при выполнении программы некоторый тип может изменяться.

С помощью указателя на объект класса Type_info пользователь

получает доступ к информации о типе из typeid и, теперь его

программа начинает зависеть от конкретной системы динамических

запросов о типе и от структуры динамической информации о нем.

Но эти средства не входят в стандарт языка, а задать их с помощью

хорошо продуманных макроопределений непросто.



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




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

    Басты бет