Что делать, если мы хотим обрабатывать коллекцию объектов класса Circle как коллекцию класса Shape, т.е. если действительно хотим, чтобы функция better() (представляющая собой вариант нашей старой знакомой функции draw_all(); см. разделы 19.3.2 и 22.1.3) реализовала полиморфизм? По существу, мы не можем этого сделать. В разделах 19.3.3 и 25.4.2 показано, что система типов имеет веские основания отказаться воспринимать тип vector как vector. По той же причине она отказывается принимать тип Array_ref как Array_ref. Если вы не помните, почему, то перечитайте раздел 19.3.3, поскольку данный момент очень важен, даже если это кажется неудобным.
a[i].draw() в функции better() противоречит этому требованию. Когда мы видим в этом выражении точку, а не стрелку (–>), следует ожидать проблем с полиморфизмом
Что нам делать? Во-первых, мы должны работать с указателями (или ссылками), а не с самими объектами, поэтому следует попытаться использовать классы Array_ref, Array_ref и тому подобные, а не Array_ref, Array_ref и т.п.
Однако мы по-прежнему не можем конвертировать класс Array_ref в класс Array_ref, поскольку нам потом может потребоваться поместить в контейнер Array_ref элементы, которые не имеют типа Circle*. Правда, существует одна лазейка.
• Мы не хотим модифицировать наш объект класса Array_ref; мы просто хотим рисовать объекты класса Shape! Это интересный и совершенно особый случай: наш аргумент против преобразования типа Array_ref в Array_ref не относится к ситуациям, в которых мы не хотим модифицировать класс Array_ref.
• Все массивы указателей имеют одну и ту же схему (независимо от объектов, на которые они ссылаются), поэтому нас не должна волновать проблема, упомянутая в разделе 25.4.2.
Array_ref будет интерпретироваться как неизменяемый объект класса Array_ref. Итак, нам достаточно просто найти способ это сделать. Рассмотрим пример
Нет никаких логических препятствий интерпретировать данный массив указателей типа Circle* как неизменяемый массив указателей типа Shape* (из контейнера Array_ref).
better() так, чтобы она использовала указатели и гарантировала, что мы ничего не напутаем с аргументами контейнера.
void better2(const Array_ref
{
for (int i = 0; i
if (a[i])
a[i]–>draw();
}
Теперь мы работаем с указателями, поэтому должны предусмотреть проверку нулевого показателя. Для того чтобы гарантировать, что функция better2() не модифицирует наш массив и векторы находятся под защитой контейнера Array_ref, мы добавили несколько квалификаторов const. Первый квалификатор const гарантирует, что мы не применим к объекту класса Array_ref модифицирующие операции, такие как assign() и reset(). Второй квалификатор const размещен после звездочки (*). Это значит, что мы хотим иметь константный указатель (а не указатель на константы); иначе говоря, мы не хотим модифицировать указатели на элементы, даже если у нас есть операции, позволяющие это сделать.
Далее, мы должны устранить главную проблему: как выразить идею, что объект класса Array_ref можно конвертировать
• в нечто подобное объекту класса Array_ref (который можно использовать в функции better2());
• но только если объект класса Array_ref является неизменяемым.
Это можно сделать, добавив в класс Array_ref оператор преобразования.
template
class Array_ref {
public:
// как прежде
template
operator const Array_ref
{
// проверка неявного преобразования элементов:
static_cast(*static_cast
// приведение класса Array_ref:
return Array_ref(p),sz);
}
// как прежде
};
Это похоже на головоломку, но все же перечислим ее основные моменты.