Б. Страуструп - Дизайн и Эволюция C++. 2006 (1160775), страница 94
Текст из файла (страница 94)
С другой стороны, я ожидаю, что потихоньку удастся избавиться от небезопасной практики работы с массивами. В стандартной библиотеке АХБ1/180 есть шаблон класса динамического массива г1упаг гау (см. раздел 8.5.), так что можно будет пользоваться либо им, либо каким-то самодельным шаблоном и не прибегать к массивам без контроля выхода за границы. Часто раздаются критические замечания, что в С и С++ не контролируются индексы массива.
В большинстве случаев это недовольство беспочвенно, поскольку люди забывают, что, хотя возможность ошибиться, выйдя за пределы массива, и есть, делать такую ошибку вовсе не обязательно. Шаблоны массивов переносят детали работы с низкоуровневыми массивами в глубь реализации, где им и надлежит находиться. По мере того как массивы в стиле С станут применяться все реже из-за того, что работа с ними будет перенесена в классы и шаблоны, число связанных с массивами ошибок резко уменьшится. Это и так постепенно происходит, а наличие шаблонов, особенно входящих в состав библиотек, ускорит процесс. Третий важный аспект работы с шаблонами состоит в том, что в сочетании с наследованием они открывают совершенно новые возможности для проектирования библиотек (см.
раздел 15.8). И!ИИИИ>611 Шаблоны Хотя компиляторов, поддерживаюгпих шаблоны, и немало, но все же они пока не стали доступными повсеместно. Да и те, что есть, еше недостаточно совершенны. 15. 11.1. Отделение реализации от интерфейса Механизм шаблонов задействован исключительно на этапах компиляции и компоновки.
Он не нуждается ни в какой поддержке во время исполнения. Конечно, это сознательное решение, но остается одна проблема: как классы и функции, сгенерированные (инстанцированные) из шаблонов, могут зависеть от информации, известной только на этапе выполнения. Обычный для С++ ответ— полъзуйтесь виртуальными функциями. Многие выражали озабоченность тем, что шаблоны слишком сильно зависят от наличия исходного кода. В этом видели два нежелательных побочных эффекта: о нельзя засекретить коммерческую программу; о если реализация шаблона изменяется, пользователь должен перекомпилировать свою программу.
Разумеется, так обстоит дело лишь в самых примитивных реализациях, но и в этом случае шаблон класса, который является производным от классов, предоставляюших ясный интерфейс, позволяет уменьшить неприятные последствия. Часто шаблон содержит просто интерфейсный код к чему-то «секретному», что можно изменять, не затрагивая интерфейс. Примеры такой техники — шаблон рчессог из раздела 15.5, шаблонная версия класса зес из раздела 13.2.2. Для решения проблем можно воспользоваться концепцией виртуальных функций, так что мне не нужно предоставлять еще один вариант таблицы переходов.
Можно также придумать форму хранения шаблонов в частично откомпилированном виде, тогда секреты производителя останутся в такой же мере безопасными, что и обычный объектный код. Для некоторых проблема состоит в том, как гарантировать, что пользователю не удастся — прямо или косвенно — инстанцировать новые версии шаблонов, которые по идее должны быть секретными. Но для этого достаточно просто не предоставлять исходный код. Данный подход реализуем, если производитель может заранее инстанцировать (см.
раздел 15 10.1) все необходимые версии. Тогда эти (и только эти) версии можно поставлять в виде библиотек объектного кода. 15. 11 2. Гибкость и эффективность Поскольку шаблоны призваны напрямую конкурировать с макросами, требования к их гибкости и эффективности очень высоки. Теперь уже можно сказать, что получившийся механизм обладает нужными свойствами в высшей степени и не жертвует при этом статическим контролем типов.
Когда дело доходит до выражения алгоритмов, мне иногда хочется иметь средства более высокого порядка, но никогда — контроль типов во время исполнения. Думается, что предлагаемые «улучшения» шаблонов за счет введения ограничений отрицательно сказались бы на их полезности, не увеличив безопасности, простоты или эффективности.
Процитирую Алекса Степанова, который подвел итог опыту написания большой библиотеки структур данных и алгоритмов; Последствия введения шаблонов ЯИИИИИИП «С++ — довольно мощный язык. Это первый язык в нашей практике, позволивший конструировать обобщенные программные компоненты, обладающий математической точностью, изяществом и абстрактностью.
При этом получающиеся компоненты не уступают в эффективности необобщенному кодуэ. Еше только предстоит раскрыть всю моп(ь сочетания шаблонов, абстрактных классов, обработки исключений и т.д. Я не думаю, что десятикратная разница в разлгере реализации компонент Буча [ВоосЬ, 1993Ъ| на С++ и на Ас(а — это нехарактерный прнлгер (см. раздел 8А.(). 15.11.3. Влияние на другие компоненты С++ Аргумент шаблона может иметь встроенный или определенный пользователем тип. Это заставляло постоянно думать о том, чтобы встроенныс и пользовательские типы выглядели и вели себя по возможности одинаково. К сожалению, добиться полного тождества между ними нельзя. Причина — оставаясь в рамках совместимости с С, невозможно исключить нерегулярность встроенных типов. Но в некоторых отношениях встроенные типы все же выиграли от результатов работы над шаблонами.
Когда я только еше думал о шаблонах и позже, когда начал их применять, то обнаружил несколько случаев, в которых встроенные типы трактовались несколько иначе, чем классы. Это стало препятствием к написанию шаблонов, которые можно было бы использовать с аргументами как встроенных, так и пользовательских типов. Поэтому была поставлена цель обеспечить одинаковое поведение всех типов в семантическом и синтаксическом отношениях.
Эта работа нс окончена и по сей день. Рассмотрим пример: уессот ч(10) г/Г вектор из 10 элементов Раньше такой синтаксис инициализации был неприменим к встроенным типам. Чтобы разрешить его, для встроенных типов введены понятия конструктора и деструктора, например: 1пт а(1); /г' до версии 2.1 ошибка, теперь инициализирует а единицей Я подумывал о том, чтобы пойти дальше и разрешить наследование от встроенных типов и явное объявление для них встроенных операторов. Но сдержался. Если разрешить наследование от 1пс, программист на С++ фактически не получит ничего принципиально нового по сравнению с членом типа 1пг. Ведь в классе 1пс нет виртуальных функций, которые производный класс мог бы заместить.
С другой стороны, правила преобразования в С настолько хаотичны, что никак не удается представить, будто 1пс, з)тоге и т.д. — обычные классы с предсказуемым поведением. Они либо сохраняют совместимость с С, либо подчиняются четким правилам С++ для классов, но не то и другое одновременно. Разрешить переопределять встроенные операторы — орегасот+ ( 1пг, 1пс ), например, — значило заодно сделать язык подверженным мутациям.
Однако идея разрешить синтез таких функций, чтобы можно было передавать указатели на них или ссылаться как-то иначе, довольно привлекательна. ~ИИЙ11 Шаблоны Концептуально встроенные типы имеют конструкторы и деструкторы. Например: Сешр1асесс1авв Т> с1авв Х ( Т а; Тпс Ь соар1ех с; рнЬ11с: Х(] з а[Т()), 1(1ае()], с(соар1ех()) ( // ): Конструктор х инициализирует каждый член, вызывая для него конструктор по умолчанию. Умавчиваемый конструктор любого типа т по определению дает то же значение, какое имела бы глобальная переменная типа т, не инициализированная явно. Это уточнение АКМ, где считалось, что Х ( ) возвращает неопределенное значение, если только для х не был определен конструктор по умолчанию.
Глава 1б. Обрабетка исключений На паникуй! Руководство яля путе~иествуввщмл по Галаятике автостопом 1б.1. Введение В первоначальном проекте С++ исключения рассматривались, но бмли отложены из-за нехватки времени на внимательное изучение вопросов проектирования и реализации, а также из опасения, что они могут сильно усложнить компилятор (см. раздел 3.15).
Было понятно, что плохой проект может вызвать большие издержки во время выполнения и намного увеличить время, необходимое для переноса на другую платформу. Исключения считались важными для обработки ошибок в программах, составленных из независимо спроектированных библиотек. Проектирование механизма исключений для С++ растянулось на несколько лет (с 1984 по 1989 гг.). Это была первая часть С++, которая проектировалась на глазах заинтересованной публики. Помимо бесчисленных обсуждений, черю которые проходило любое включенное в С++ средство, несколько вариантов были изложены в статьях и подверглисыпирокому обсуждению.
На последних зтапах активное участие в работе принимал Эндрю Кениг, вместе которым мы написали статьи [Коешй, 1989а! и [Коешй, 19901. Существенные фрагменты окончательной схемы были разработаны по пути на конференцию ПБЕН1Х по С++, которая состоялась в ноябре 1987 г. в Санта-Фе. Промежуточные варианты проекта излагались также на встречах в компаниях Арр1е, ПЕС (Спринг Брук), Мюгозой, 1ВМ (Альмадена), 8пп и в других местах. Я нашел людей, имевших реальный опыт работы с системами, поддерживающими обработку исключений. Первое серьезное обсуждение обработки исключений в С++ состоялось в Оксфорде летом 1983 г.
Основные темы беседы с Тони Вильямсом (Топу%!1!!ашз) из Лаборатории Резерфорда — проектирование отказоустойчивых систем и ценность статического контроля типов в механизме обработки исключений. К моменту начала дебатов по поводу обработки исключений в комитете АН81 С++ опыт их использования в языке ограничивался библиотечными реализациями, предпринятыми компанией Арр!е, Майком Миллером [М111ег,19881 и некоторыми другими. Единственную реализацию на уровне компилятора выполнил Майк Тиман [Т!ешапп, 1990!. Это вызывало беспокойство, хотя в общем все были согласны, что в какой-то форме обработка исключений очень пригодилась бы в С++. В частности, Дмитрий Ленков, основываясь на опыте компании Нету!е11- Рас!саго, выразил горячее желание иметь такой механизм.
Только Дуг Макилрой считал, что наличие обработки исключений понизит надежность систем, поскольку Обработка исключений ПИИИИИИИ авторы библиотек и другие программисты станут просто возбуждать исключения, не пытаясь понять, в чем проблема и как устранить ее. Время покажет, в какой мере оправдалось пророчество Дуга. Естественно, что никакое языковое средство не может запретить программисту писать плохие программы. Первые реализации обработки исключений в том виде, который описан в АКМ, начали появляться весной 1992 г.