Анимация
JavaScript
|
Главная Библионтека Виртуальный Shape Виртуальный
RoundedRectangle Рис. 11.2. "Бриллиантовая" иерархия классов, использующая виртуальное наследование Возможно, это не самая изящная иерархия классов, но ведь мы никогда не знаем, что может понадобиться пользователю. Существуют вполне реальные ситуации, в которых необходимы именно "бриллиантовые" иерархии классов, несмотря на все их недостатки. Следовательно, двойной диспетчер, определенный нами, должен работать с такими иерархиями. На самом деле диспетчер по-прежнему вполне справляется с заданием. Однако, если заменить оператор dynami c cast оператором static cast, при попытке привести тип shape& к любому из типов Rectangle*, Roundedshape& или RoundedRectangle* возникнет ошибка компиляции. Это происходит потому, что виртуальное наследование отличается от простого наследования. Виртуальное наследование предоставляет средства, позволяющие нескольким производным классам совместно использовать один и тот же объект базового класса. Компилятор не может просто поместить объект производного класса в память, намертво связав его с объектом базового класса. В некоторых реализациях множественного наследования каждый производный класс хранит указатель на свой базовый объект. Во время приведения производного типа к базовому компилятор использует этот указатель. Однако объект базового класса не хранит указатель на объекты производных классов. С прагматичной точки зрения все это значит, что после приведения объекта производного типа к виртуальному базовому типу оказывается, что механизма, позволяющего вернуться обратно к объекту производного типа, компилятор не имеет. Невозможно выполнить оператор static cast для перехода от объекта виртуального базового типа к объекту производного типа. Однако оператор static cast использует более сложные средства для распознайа-ния отношений между классами и прекрасно работает даже с виртуальными базовыми типами. Короче говоря, для иерархий, использующих виртуальное наследование, следует применять оператор dynami c cast. Вторая ситуация возникает, когда иерархия классов использует цростое (даже не виртуальное) множественное наследование. class shape { ... }; class Rectangle : public shape {...}; class RoundedShape : public Shape {...}; class RoundedRectangle : public Rectangle, public RoundedShape { ... } Ha рис. 11.2 показаны отношения между классами, входящими в эту иерархию.
RoundedRectangle Рис. 11.3. "Бриллиантовая" иерархия классов, использующая невиртуальное наследование Несмотря на то что форма иерархии осталась прежней, структура объектов значительно отличается от предыдущей. Объекты класса RoundedRectangle теперь могут быть двумя разными подобъектами типа Shape. Это значит, что преобразование объекта типа RoundedRectangle в объект типа Shape становится неоднозначным. Какой тип Shape имеется в виду - тот, который относится к типу RoundedShape, или тот, который связан с типом Rectangi е? Аналогично мы не можем выполнить даже статическое приведение типа Shape& к типу RoundedRectangle*, поскольку компилятор не знает, какой из подобъектов типа Shape имеется в виду. Что делать? Рассмотрим следующий код. RoundedRectangle roundRect; Rectangle* rect = roundRect; Shape* shapel = rect; RoundedShape* roundshape Однозначное неявное преобразование roundRect; Однозначное неявное преобразование Shape* shape2 = roundshape; SomeDispatcher d; Shape* sotneOthershape = ...; d.GoCshapel, sotneOthershape); d.GoCshape2, sotneOthershape); Здесь важно то, что диспетчер использует оператор dynami c cast для преобразования типа Shape* в тип RoundedShape*. Если попытаться зарегистрировать трамплинную функцию для такого преобразования, возникнет ошибка компиляции. Если диспетчер использует оператор динамического приведения типов, никаких проблем вообше не возникает. Оператор dynami c cast<RoundedRectangle*> применяется к любым подобъектам базового класса shape, позволяя получить правильный объект. Очевидно, что лучше динамического приведения типов ничего нет. Оператор dynami c cast разработан для получения правильных объектов в иерархии классов, независимо от сложности их структуры. Итак, оператор static cast нельзя применять для двойной диспетчеризации иерархии классов, в которую базовый класс входит несколько раз, независимо от того, виртуально наследование или нет. Может показаться, что оператор dynamic cast следует применять при создании любых диспетчеров. Однако существуют два дополнительных момента. • Очень немногие реальные иерархии классов используют "бриллиантовую" форму наследования. Такие иерархии слишком сложны, и проблемы, которые они порождают, перевешивают их достоинства. Поэтому разработчики обычно избегают применять такие иерархии классов. • Оператор dynami c cast выполняется намного медленнее, чем оператор static cast. Его мощь достигается за счет снижения быстродействия. Многие клиенты работают с очень простыми иерархиями классов и хотят, чтобы их профаммы работали быстро. Использование оператора dynamic cast ставит клиентов перед выбором: разработать совершенно новый диспетчер или внести изменения в библиотеку. В библиотеке Loki реализована стратегия CastingPolicy. Она представляет собой шаблонный класс с двумя параметрами: исходный и результирующий типы. Стратегия содержит единственную статическую функцию Cast. Ниже приводится определение класса DynamicCast. template <c1ass To, class From> struct DynamicCaster static t0& Cast(From& obj) return dynamic cast<To&>(obj); Диспетчеры FnDispatcher и FunctorDispatcher используют стратегию Casting-Policy, следуя принципам, сформулированным в главе I. Ниже приводится модифицированный класс FnDispatcher (изменения выделены полужирным шрифтом). template < class BaseLhs, class BaseRhs,= BaseLhs, ResultType = void, template <class, class> class CastingPolicy = DynamicCaster > class FunctorDispatcher { template <class SomeLhs, class SomeRhs, class Fun> void Add(const Fun& fun) { class Adapter : public FunctorType::Impl { Fun fun ; virtual ResultType operatorO(BaseLhsA Ihs, BaseRhsA rhs) return fun ( CastingPolicy<SomeLhs, BaseLhs>::Cast(lhs), CastingPolicy<SomeRhs, BaseRhs>::Cast(rhs); 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 [ 95 ] 96 97 98 99 100 101 102 103 104 105 |