Программирование >>  Оптимизация возвращаемого значения 

1 ... 55 56 57 [ 58 ] 59 60 61 ... 96



(jr.sctlG CD

(sSingle

Рис. 5.5

Рассмотрим теперь следующий код:

template<class Т> Как и выше, шаблон

class SmartPtr {...}; функции-члена для операций

преобразования, void displayAndPlay(const SmartPtr<MusicProduct>& pmp,

int howMany); void displayAndPlay(const SmartPtr<Cassette>& pc,

int howMany) ;

SmartPtr<CasSingle> dumbMusic (new CasSingle ( Achy Breaky Heart ) ) ; displayAndPlay(dumbMusic, 1); Ошибка!

В этом примере функция displayAndPlay перегружается: в одном сл5ае функцией, принимающей объект Smart Ptr<MusicProduct>, а в другом - функцией Smart Ptr<Cassette>. Если вы вызываете функцию displayAndPlay для SmartPtr<CasSingle>, то ожидаете, что будет выбрана функция Smart-Ptr<Cassette>, поскольку CasSingle прямо наследует от Cassette и только неявно от Mus icProduct. Конечно, так оно и должно быть для обычных указателей.

SmartPtr<MusicProduct> будет успешным, так же как и преобразование SmartPtr<Cassette> в SmartPtr<MusicProduct>. Вуаля! Неявное преобразование типов интеллектуальных указателей. Что может быть проще?

Более того, что может быть более мощным? Пусть вас не вводит в заблуждение рассмотренный пример - не нужно думать, что этот метод работает только для преобразования указателей в иерархии наследования. Он применим для любых неявных преобразований типов указателей. Если имеется обычный указатель типа Т1 * и другой обычный указатель типа Т2 *, то интеллектуальный указатель на Т1 разрешается неявно преобразовать в интеллектуальный указатель на Т2 тогда и только тогда, когда можно неявно преобразовать Т1* в Т2 *.

Этот метод дает почти такое же поведение, которое вам необходимо. Предположим, что вы добавите к иерархии MusicProduct новый класс CasSingle, представляющий синглы на кассетах. Иерархия примет вид, изображенный на рис. 5.5.



Но, увы, наши интеллектуальные указатели не настолько умны. Они используют функции-члены в качестве операторов преобразования, а с точки зрения компиляторов C-I-+ все вызовы функций преобразования равноценны. В результате вызов функции displayAndPlay дает неопределенный результат, так как преобразование из SmartPtr<CasSingle> в SmartPtr<Cassette> ничем не определено.

Реализация преобразования интеллектуальных указателей при помоши шаблонов функций-членов имеет еше два недостатка. Во-первых, поддержка шаблонов функций-членов встречается редко, поэтому данный метод не всегда может применяться в других системах. Во-вторых, логика работы далеко не прозрачна, и будет понятна только тем, кто хорошо знает правила соответствия аргументов вызовов функций, функций неявного преобразования типов, неявного создания экземпляров функций из шаблонов и сушествования шаблонов функций-членов. Жаль бедного программиста, который никогда не видел данный прием и которому требуется поддерживать или дополнять основанный на нем код. Этот способ, несомненно, ловко придуман, но здесь есть опасность перехитрить самого себя.

Не будем ходить вокруг да около. В действительности вам нужно знать, можно ли сделать так, чтобы классы интеллектуальных указателей вели себя подобно обычным указателям при преобразованиях типов, основанных на наследовании. Ответ прост: нет, нельзя. Как заметил Дэниэл Эдельсон (Daniel Edelson), интеллектуальные указатели интеллектуальны, но это не указатели. Jljmee, что можно сделать - применять для создания функций преобразования шаблоны функций-членов, а если возникает неоднозначность - использовать операторы приведения типа (см. правило 2). Это не самый лучший выход, но он достаточно хорош. Кроме того, необходимость иногда включать в код операторы приведения - невысокая цена за сложную функциональцость, которую могут обеспечивать интеллектуальные указатели.

Интеллектуальные указатели и атрибут const

Как вы помните, для обычных указателей атрибут const может относиться к самому указателю; тому, на что он указывает; или и к тому, и другому:

CD goodCD( Flood );

const CD *p; p - He-const указатель

на const объект CD.

CD * const p = &goodCD; P const указатель

на не-const объект CD; так как р - const, нужна его инициализация.

const CD * const р = ScgoodCD; р - const указатель

на const объект CD.

Естественно, вам бы хотелось, чтобы интеллектуальные указатели были столь же гибкими. К сожалению, атрибут cons t может относиться только к указателю, а не к объекту, на который он указывает:



const SmartPtr<CD> p = - const интеллектуальный

&goodCD; указатель на не-const

объект CD.

Кажется, что это легко исправить, просто создав интеллектуальный указатель на const объект CD:

SmartPtr<const CD> р = р - не-const интеллектуальный

&goodCD; указатель на const объект CD.

Теперь можно создать четыре искомые комбинации атрибутов const для объекта и указателя:

SmartPtr<CD> р; He-const объект,

He-const указатель. SmartPtr<const CD> р; const объект,

не-const указатель, const SmartPtr<CD> р = &goodCD; He-const объект,

const указатель, const SmartPtr<const CD> p = &goodCD; const объект,

const указатель.

Увы, в этой бочке меда есть ложка дегтя. Если используются обычные указатели, можно инициализировать const указатели при помощи не-const указателей и указатели на const объекты при помощи указателей на не-const объекты; правила присвоения не изменяются. Например:

CD *pCD = new CD( Famous Movie Themes );

const CD * pConstCD = pCD; Нормально.

Ho посмотрите, что произойдет, если сделать то же самое при помощи интеллектуальных указателей:

SmartPtr<CD> pCD = new CD( Famous Movie Themes ); SmartPtr<const CD> pConstCD = pCD; Нормально ли?

Типы SmartPtr<CD> и SmartPtr<const CD> совершенно различны. С точки зрения компиляторов они не связаны между собой, поэтому нельзя полагать, что они совместимы по принципу присваивания. Раньше единственный способ обеспечить совместимость заключался в создании функции для преобразования объектов типа SmartPtr<CD> в объекты типа SmartPtr<const CD>. Если ваш компилятор поддерживает шаблоны функций-членов, вы можете использовать приведенный выше метод автоматического формирования необходимых операторов неявного преобразования типов. (Я уже отмечал, что этот метод успешен всегда, когда работает соответствующее преобразование для обычных указателей. Преобразования, включающие const, не являются исключением из данного правила.) Если же ваш компилятор не поддерживает такие шаблоны, вам придется пройти еще через одно испытание.

Преобразования, включающие const, - это улица с односторонним движением: можно безбоязненно переходить от не-const к const, но небезопасно от const к не-const. Кроме того, все, что можно делать с const указателем, можно



1 ... 55 56 57 [ 58 ] 59 60 61 ... 96

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика