Философия программирования Win95. Глава 4

Разное

Отсканированная мной книга Лу Гринзоу "Философия программирования Windows 95/NT", глава 4. Обсуждение, исправление опечаток и вопросы - в комментариях.

Инструменты


Гизмонавт - сущ. м. 1. личность, склонная воспользоваться
любым новшеством просто потому, что эта вещь новая, не
обращая внимания ни на ее потребительские качества, ни на
ее уместность в конкретных условиях работы, ни на стои-
мость ее адаптации. 2. (прогр.) любой участник вашего
проекта, который хочет использовать новый инструмент вме-
сто старого, который вы использовали годами потому, что он
явно превосходит новый.
 [Транслитерация термина «gizmonaut», по-видимому, придуманного
автором; происходит от жаргонизма «gizmo», близкого к таким
русским жаргонизмам, как «наворот», «прибамбас» и т. п. ].

Нео-луддит - сущ. м. 1. личность, отказывающаяся перейти от
старых, неэффективных инструментов и технологий к но-
вым, более эффективным, и пытающаяся оправдать свое по-
ведение благовидными аргументами, уводящими в сторону от
темы. 2. (прогр.) любой участник вашего проекта, который
не хочет расстаться со старым инструментом в обмен на но-
вый, который вы только что нашли и который очевидно
превосходит старый.


Компиляторы и редакторы, отладчики и библиотеки.
Игрушки, примочки, прибамбасы, штучки-дрючки.
Всякая чепуха.
Инструменты.

Немного есть предметов обсуждения, более близких и дорогих
программерскому сердцу, чем инструменты. А почему собственно им не быть
таковыми? Первая струйка воздуха с запахом поликарбоната из свеже-
открытого ларчика с CD-ROM... Нетерпеливые метания мышиного курсора по
диалогам программы-инсталлятора... Волнение при перелистывании послед-
него номера каталога «Программистская Феерия» и трепетное предвкушение,
что вот именно в этот раз, наконец-то, вы сможете найти тот золотой ключик,
который повысит вашу производительность, сведет частоту ваших ошибок к
мизерному уровню и улучшит цвет вашего лица... Ну кто может желать чего-
то большего?

Если инструменты так важны для нас (а они точно важны, поскольку все
вместе образуют главное связующее звено, своего рода «церебральный
интерфейс» между нашими умами и нашими проектами), если мы так любим
их и так часто говорим о них, то почему же так много программистов так
потрясающе глупо и неумело их себе подбирают? Опыт моих проектов по
разработке программного обеспечения (среди которых были и новые версии
операционной системы для высокопроизводительных ЭВМ, и незначительные
изменения маленьких условнобесплатных программ) показывает, что большин-
ство программистов являются либо гизмонавтами, либо нео-луддитами. Иными
словами, огромное множество людей выбирает себе инструменты, основываясь
на неверных доводах и мотивах. А нейтральная полоса, разделяющая два упо-
мянутых выше полюса, мягко говоря, очень мала и довольно сильно
разбросана.

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

Что такое «инструмент программирования» и почему он так важен

Инструмент программирования состоит не только (и не столько) из
существительных - предметов и продуктов, но и из глаголов - процессов и
методов. Поэтому и выбор инструмента происходит не только на макроуровне
(«Используйте Delphi/32 вместо VC++»), но и на микроуровне («Отключите
оптимизацию у этого компилятора»). К инструментам следует причислить ап-
паратное обеспечение, программное обеспечение и информацию - все, что вы
прямо или косвенно используете в своей работе. Далее сервер вашей локальной
сети, расположенный в зале ниже этажом, является вашим инструментом, не-
смотря на то, что он не стоит на вашем столе. Даже полузабытый обрывок ка-
кой-то давней дискуссии с коллегами вносит свой вклад в ваш инструментарий.

На самом деле, все ваши знания, включая опыт проектирования, кодирова-
ния и тестирования, являются отдельным, самым важным вашим инструмен-
том, хотя многие программисты не думают о них в таком ключе (если вообще
что-то думают). Стоит вам только перешагнуть ступеньку «Неllо, World», и вы
окажетесь в глубоком, холодном океане, где вашим единственным оружием
против акул будет лишь ваша собственная сообразительность.

Тем временем, все без исключения производители программистского
инструментария не устают навязывать свои IDE-матики, и с любовью со-
ставляют статьи, рекламные проспекты и выставочные демонстрации: «Взгля-
ните! Всего шесть щелчков мышью, и вы тоже сможете создать программу с
предварительным просмотром печати, диалогом About, и общими диалогами -
и все это без единой набранной вручную строки кода!» Теперь я с нетерпением
жду, когда они начнут прилагать к каждой копии своей IDE набор столовых
ножей, салатницу и волшебную щетку для чистки автомашины.

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

В Ваш набор инструментов непосредственно определяет, что сможет де-
лать ваша программа, а что нет. Например, попробуйте отреагировать
на некоторые Windows-сообщения в Visual Basic 3.0 - и вы об-
наружите, что это невозможно (по крайней мере, без помощи хитроум-
ных третьесторонних примочек к VB). Для многих программистов это
не является проблемой, и они успешно создают при помощи Visual Ba-
sic замечательные программы. Но для других это   - шоу-стоппер.

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

В Выбор инструментария непосредственно влияет на временные показа-
тели кодирования и тестирования. Возможно, это самый традицион-
ный и очевидный пример анализа соотношения «затраты/выигрыш»,
но он вытерпит очередной повтор, поскольку слишком многие, судя по
всему, игнорируют его суть: если инструмент стоимостью 400 долларов
обеспечит' вам экономию времени (конечно, с учетом временных затрат
на его освоение) в размере хотя бы нескольких часов на каждый цикл
разработки, то, вероятно, имеет смысл этот инструмент приобрести.
(Существуют исключения из этой упрощенной схемы анализа, о ко-
торых я вскоре расскажу подробнее.)

В Windows в значительной степени усиливает остроту проблем, связан-
ных с инструментами. Программисты балансируют на очень высокой,
очень узкой и не слишком твердой площадке внутри башни, составлен-
ной из многочисленных построений, интерфейсов и инструментов. Эта
башня насквозь пропитана ошибками, сбоями, недокументированными
побочными эффектами и паршивой документацией. Временами меня
посещает одна дикая мысль: это просто чудо - когда что-то более
сложное, чем «Неllо, World», вообще как-то работает.

Рекомендации по выбору и использованию инструментов

Теория - чудесная вещь, но ею не прокормить и собаку. Когда вы листаете
каталог программистского инструментария и когда вы сталкиваетесь с, каза-
лось бы, бесконечным ассортиментом предлагаемых вам продуктов, вам
определенно необходим какой-нибудь надежный метод принятия конкретных
(и правильных!) решении о покупке.

Сожалею, но я не смогу помочь вам в принятии всех ваших решений. Увы,
я даже не могу уберечь самого себя от совершения редких, но поразительных
промахов при выборе инструментов. Но у меня все же есть несколько рекомен-
даций, которые, я надеюсь, должны помочь вам провести ваше судно через под-
стерегающие за каждым поворотом пороги.

0. Исследуйте и эксплуатируйте

Исследование означает, что ваш кругозор в области новых инструментов и
технологий должен постоянно расширяться, и что ваш ум должен все время ос-
таваться восприимчивым к информации о новинках в области ваших профес-
сиональных интересов. Присматривайтесь ко всему, что проходит мимо вас -
к каждой библиотеке и к каждому программному приспособлению, для
которых у вас найдется время. Заставляйте ваше руководство покупать
оценочные версии (или сами доставайте бесплатные копии у поставщиков; не-
которые обязательно предложат такие копии при соответствующих условиях),
просто внимательно читайте все найденные обзоры и пресс-релизы об
инструментарии для разработчиков. Исследование должно быть бурной, сво-
бодной, беспорядочной (в разумных пределах) частью процесса отбора
инструментов, который никогда не закончится, потому что новые и новые кан-
дидаты будут вечно выходить на рынок.

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

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

Помимо сложных многофункциональных продуктов, всегда ищите
хорошие узкоспециализированные инструменты, предназначенные для реше-
ния частных, относительно обособленных задач. Например, если вы хотите
включить в вашу программу приличный текстовый редактор, не надо писать его
самостоятельно. Если вас не интересует совместимость с Windows 3.1, исполь-
зуйте новый элемент управления Windows 95 форматированное поле
редактирования. Если же такая совместимость вам необходима, познакомьтесь
с одним из нескольких существующих третьесторонних компонентов, предос-
тавляющих примерно те же возможности. В любом случае, именно такие узко-
специализированные инструменты становятся примерами наиболее удачного
выбора: вы получаете инструмент, который делает только одну вещь, но делает
ее настолько хорошо, что одна из частей вашего проекта вмиг перестает быть
проблемой.

Изучая возможности отбираемых инструментов, не забывайте оценивать и
их стоимость, которая отнюдь не сводится только к цифре, указанной на цен-
нике продукта. Например, если производитель компилятора заявляет, что для
использования его продукта вам необходимо иметь 16 Мб памяти, то
хорошенько подумайте: в самом ли деле вы будете чувствовать себя комфортно,
работая на 16 Мб, или есть вероятность того, что уже на следующий день после
компиляции чего-нибудь более серьезного, чем «Hello, World», вы начнете от-
кладывать деньги на покупку еще 8 или 16 Мб?

До этого момента вы все еще работали на макроуровне и исследовали
целую вселенную доступного вам инструментария. И все ваши решения на этом
уровне касаются вопроса, какие инструменты выбрать.

Как только вы делаете свой выбор - вот этот компилятор, вот те библио-
теки, вот тот третьесторонний отладчик - процессы исследования и эксплуа-
тации переходят в новую фазу, на микроуровень. Теперь вы интенсивно экс-
плуатируете возможности и свойства конкретных, отдельных продуктов и уже
немного иначе ставите вопрос: как использовать каждый из выбранных
инструментов. Точно так же, как и на макроуровне, вы должны выяснить,
опробовать и оценить абсолютно все, что может делать конкретный продукт. И
как только наступит момент принятия окончательного решения о том, как
именно будет использоваться тот или иной инструмент, вы должны нацепить
ваши виртуальные темные очки и превратиться в ужасно несговорчивого поку-
пателя: заставить каждую функцию, каждое свойство инструмента
неопровержимо доказать вам свою ценность, корректность и надежность.

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

Плакат, который следует поставить рядом с компьютером каждого
программиста: «ООП - это хорошо, ООП - наш друг, но ООП - не
панацея».

Однажды Ларри Константин заметил, что ООП - это, прежде всего,
хорошие упаковка и оформление. Да, это так. Но голая правда состоит в
том, что дополнительные усилия, необходимые для этой упаковки и
оформления, просто не окупаются. Что бы ни говорили производители
программ и инструментов, у ООП есть оборотная сторона - излишняя
сложность. Иногда оптимальное решение вовсе не требует никаких объек-
тов: нужно просто весь код и все данные, необходимые для выполнения
некоторой логической задачи, поместить в отдельную единицу компиля-
ции, а затем выставить напоказ одну единственную функцию, которую и
смогут вызывать другие части программы. Именно такой подход исполь-
зован в программе-примере из главы 8 на стр. 271: весь код и данные мо-
дуля CHECKER можно было бы запросто превратить в объект, но что мы
имели бы с этого? По-моему, абсолютно ничего. Все, что может когда-ни-
будь понадобиться (или захотеться) вызывающей программе от этого ко-
да, - это вызвать функцию GoodCRC() и исследовать возвращенное ею
значение. Поэтому нет никакой нужды вертеть в руках классы, конструк-
торы, деструкторы и прочее.

Подобное изолирование кода и данных внутри отдельной единицы компи-
ляции, на самом деле, эквивалентно выполнению одного из принципов
ООП, но без использования всех его дорогостоящих наворотов. Возмож-
но, следует дать этому подходу название «ОБК», означающее «ООП без
классов».

Не поймите меня неправильно, я - убежденный сторонник концепций
объектно-ориентированного дизайна, но в то же время я прекрасно знаю,
как их использование может вылиться в настоящую стрельбу из пушки но
воробьям. Слишком многие программисты смотрят на ООП, как на бле-
стящий новенький молоток, а на распростертый перед ними мир - как на
необъятный лес из гвоздей.

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

Учитывайте все кратковременные и перспективные проблемы. Не забы-
вайте оценивать стоимость возможного в будущем отказа от использования того
или иного инструмента. Такой отказ может оказаться весьма трудным и дорого-
стоящим. Да, какой-то новый стандартный интерфейс в Windows (MAPI, TAPI
и т. п.) кажется вам замечательно полезным. Но что, если вы примете его, пус-
тите в дело, привяжетесь к нему, а потом выясните, что он не позволяет вам
сделать очередной шаг вперед?

Удостоверьтесь, что продукт и в самом деле делает то, что обещает, и что
он обеспечивает хорошее отношение затраты/выгода. Я не говорю, что суще-
ствуют такие производители програмного обеспечения, которые лгут, но вам
лучше быть настороже, на всякий пожарный.

Если это возможно, обязательно рассмотрите документацию,
прилагающуюся к продукту. Помните, что во многих случаях документация
бывает серьезным слагаемым в оценке инструментария (если не множителем).
Если продукт кажется вам идеально подходящим для ваших целей, но у него
нет документации (или есть, но плохая), поищите какую-нибудь другую,
третьеcтороннюю доступную документацию. Конечно, это выглядит несколько
глуповато - покупать книжку для того, чтобы можно было эффективно ис-
пользовать крутой новомодный программный продукт, но кого это волнует?
Смысл всего процесса выбора инструментария - в его результатах, а не в ло-
гике.

Помните и еще одну важную вещь: тот, в чью могилу рядом с гробом
торжественно положат больше всего инструментов, отнюдь не будет победите-
лем. Знание и использование лишь 10-ти процентов возможностей ваших 50-ти
инструментов - это неэффективно и неэкономично. Какова ваша истинная
цель? Стать обладателем самого быстрого, самого большого винчестера, битком
набитого программными продуктами? Или все-таки стать как можно более
эффективным, более умелым программистом?

 

1. Добивайтесь симбиоза ваших инструментов

Выбирая себе инструменты, следите за тем, как они работают совместно.
Симбиоз инструментов - это нечто большее, чем просто совместимость исход-
ных или объектных файлов, о которой я уже упоминал выше.

Например, вам следует всегда держать под рукой печатную копию ваших
исходных текстов (уже стабилизированных), далее если на это тратится много
бумаги и печатного порошка. Почему? Не только потому, что распечатка
зачастую служит сподручным справочником, но и потому, что она может по-
служить и страховочной копией в критической ситуации. В век недорогих
сканеров и хороших OCR-программ (OCR - оптическое распознавание симво-
лов), такая распечатка может спасти вас, когда ваш винчестер сломается и вы
вдруг обнаружите, что магнитная лента со страховочной копией больше не
читается (или что кто-то забывал делать страховочную копию вашего раздела
в течение последних четырех месяцев).

Другой пример - использование индексирующего и поискового программ-
ного обеспечения (например, такого, как Isys от Odissey Development Corpora-
tion) для работы с CD, набитыми разнообразными примерами исходных тек-
стов. Вы можете по дешевке (всего 50 долларов и меньше за диск) накупить
много таких дисков, но поиск информации в них без каких-либо дополнитель-
ных средств может стать настолько трудным и долгим делом, что сведет на нет
смысл их покупки. (Иногда к подобным дискам прилагается какое-нибудь
простенькое информационно-поисковое программное обеспечение, но, к сожа-
лению, эти программы, как правило, несовместимы ни друг с другом, ни с
форматами «чужих» каталогов на CD-ROM.) Вот тут-то и может весьма приго-
диться более универсальный инструмент, подобный упомянутому выше Isys:
построив индексы ваших многочисленных каталогов, вы сможете в считанные
секунды осуществить поиск нужных ключевых слов, а затем быстро достать со-
ответствующие файлы. Кстати говоря, в один ряд с такими «каталогами исход-
ников» можно поставить и CD с дистрибутивами компиляторов - я, разуме-
ется, имею в виду огромные залежи примеров с исходными текстами,
изобилием которых славится почти каждый современный компилятор. Вы ска-
жете, что в подобных ситуациях можно с тем же примерно успехом использо-
вать утилиту grep или функцию многофайлового поиска в любимом редакторе.
Да, можно, но тогда вам придется выбирать между установкой всех примеров
на ваш жесткий диск и осуществлением поиска прямо на CD-ROM. Хмм, в об-
щем-то, это тоже вариант...

Еще одна область применения индексирующих программ - работа с ва-
шими собственными накоплениями исходного кода. Признаюсь, я лично
являюсь настоящей архивной крысой (разумеется, специализирующейся на
архивах исходных текстов) и подозреваю, что в этом я похож на многих своих
собратьев но профессии: скачиваю исходники, найденные на BBS, в Интернете
или в конференциях, копирую их с гибких дисков, приложенных к книгам и
журналам, и т. д. и т. п. Индексирование этой «копилки» - очевидно, необ-
ходимая вещь. Кстати, ее индексирование может оказаться особенно эффектив-
ным, если индексирующая программа умеет должным образом работать с ZIP-
архивами - тогда вы сможете сэкономить место на вашем диске, не теряя воз-
можности быстрого доступа к отдельным файлам (а заодно обойти известную
проблему с потерей дискового пространства из-за фиксированного размера кла-
стеров файловой системы, о которой я подробно рассказывал в главе 2 на
стр. 69).

Одним из залогов желаемого симбиоза является использование таких
инструментов, которые могут принимать и выдавать данные в чисто текстовом
виде (помимо своих «родных» форматов ввода/вывода/хранения данных).
Например, предназначенный для рисования различных диаграмм и блок-схем
программный пакет allCLEAR без проблем примет от вас чисто текстовое опи-
сание блок-схемы и по нему построит ее графическое изображение. Это позво-
ляет вам без труда расширять возможности ваших собственных программ за
счет allCLEAR. Например, вам может понадобиться написать программу,
которая читает внутренний телефонный справочник вашей компании и строит
по его данным организационную схему. Было бы слишком долго и дорого пи-
сать программу, самостоятельно выполняющую все рисунки в графике. Но при
наличии такого «уживчивого» инструмента, как allCLEAR, этого и не требу-
ется: вашей программе достаточно выдать результат в текстовом виде, а всю ос-
тальную работу за нее сделает allCLEAR. Говоря обобщенно, вы можете рас-
сматривать текстовые файлы как промежуточный язык, наиболее подходящий
для общения между разнообразными специализированными инструментами.

2. Не разбрасывайтесь и не переоценивайте сваи силы

Независимо от размера вашей программистской команды - состоит она из
вас одного или из 1000 человек, старайтесь честно и непредвзято оценить тот
уровень мастерства, который необходим для выполнения ваших проектов и ис-
пользования ваших инструментов. Например, если вы собираетесь применить
для написания программы Visual Basic 3.0 и, в частности, изготовить для этой
программы несколько компонентов VBX, это у вас не получится (при наличии
опыта работы только с Visual Basic); поскольку VBX-компоненты нельзя напи-
сать при помощи самого Visual Basic, вам еще понадобится знание C/C++ или
Pascal, а также необходимый для применения этих языков инструментарий. То
есть, вам понадобится целый набор инструментов и, что более важно, целый
арсенал навыков работы с ними.

Более-менее точная оценка реального уровня мастерства собранной ко-
манды может быть весьма трудным мероприятием. Печальную славу имеет
склонность многих программистов заявлять, что они «знают» тот или иной
язык программирования (или инструмент разработки) в то время, как на самом
деле они потратили на него лишь несколько праздных часов, да и то около года
назад. Кстати, если при этом программист неосторожно добавляет что-то типа
«но я хотел бы изучить его (язык, инструмент и т. д.) еще поглубже!», в боль-
шинстве случаев можно быть уверенными, что вас хотят провести, а заявлен-
ные программистом «знания» - лишь предлог и попытка программиста
увернуться от текущей, возможно неприятной для него работы. Когда подоб-
ные уловки удаются, серьезные несчастья в проекте почти гарантированы: ко-
манда формируется, планы и расписания фиксируются, и только спустя не-
сколько месяцев вдруг обнаруживается, что ведущий программист на С++,
ответственный за самую критичную часть проекта, на самом деле никогда
всерьез не использовал этот язык, хотя С++ и казался ему таким интересным
но книжным и журнальным статьям.

3. Не будьте слишком доверчивы к вашим поставщикам

Некоторые полагают', что нужно рассматривать только технические
характеристики инструмента, а на такие вещи, как финансовое здоровье их
производителя или его политика поддержки пользователей (то бишь вас),
можно не обращать внимания. Это - огромная ошибка, особенно при раз-
говоре о публичном программном обеспечении. Если у компании - производи-
теля вашего инструмента - имеется длинный список заброшенных продуктов,
или если у вас. есть основания полагать, что они могут так поступить при каких-
то условиях, значит у вас уже как минимум на одну проблему больше. Если вы
посмотрите врезку «Что будет с OWL?» на стр. 185, то вы найдете там не-
которые подробности о моем личном опыте в этой области.

Как же разобраться, каким поставщикам можно доверять? Вам придется
начать с доверия к вашей собственной интуиции и, конечно же, к вашим кол-
легам по профессии. Однако учтите, что в данном случае недостаточно просто
прислушиваться к публичным дискуссиям в Интернете. Потратьте некоторое
время па них, и вы услышите как пылкие славословия, так и язвительные об-
винения в адрес практически каждого производителя, более-менее известного в
этом бизнесе. Максимум того, что можно сделать в этом гаме - приложить ухо
к земле и попробовать разобраться, какие жалобы действительно обоснованны
и имеют значение для вашей работы, а какие являются лишь сотрясением воз-
духа.

Так же, как в рекомендации 0 на стр. 150 этой главы, я советую вам и в
данном вопросе не забывать про его микроуровневые проявления. Несмотря на
то, что вашей основной задачей должна быть минимизация количества сторон-
них производителей, на чьи продукты полагаются ваши проекты, не следует
слишком зацикливаться на отдельном продукте. Например, иногда бывает
правильнее использовать компилятор одного производителя, а библиотеку
классов - другого. Разумеется, основной причиной подобного выбора должно
быть только максимальное соответствие нуждам вашего проекта именно такой
комбинации инструментов.

Как можно более скептически относитесь к особо кричащим и выставляе-
мым напоказ возможностям продукта. Однажды Лили Томлин сказала, что от
этого трудно удержаться даже закоренелым циникам. Судя по ее словам, она,
наверное, занималась программированием лишь в свободное от работы время.

Всякий раз, когда вы изучаете возможности очередного инструмента, в ва-
шем мозгу обязательно должен сидеть маленький червячок и постоянно
проверять, не является ли то, что вы видите, подозрительно похожим на бес-
платный сыр. Как говорится, «если что-то выглядит слишком хорошим, чтобы
быть правдой, то, вероятно, так оно и есть». Практически у каждого компи-
лятора С++ есть такая подозрительная возможность - оптимизация (подроб-
ный разговор о ней, а также об ошибочности многих общепринятых суждений
о ней, вы найдете в разделе «Компиляторы» на стр. 164 этой главы).

В заключение, последний совет: не слишком доверяйте проектировочным
решениям вашего поставщика. Как раз во время написания этой книги я изучал
один продукт, который превосходно решал одну из проблем в моем текущем
проекте. Это был отличный пример узкоспециализированного инструмента.
Но, к сожалению, в итоге я был вынужден отказаться от его использования:
проблема, которую он так успешно снимал, была довольно незначительной
частью всего проекта, а инструмент требовал включения в финальную версию
моей программы нескольких внешних файлов с данными (не давая возможно-
сти включить их в ресурсы). Такой дизайн неприемлем для публичной
программы - выигрыш от небольшого усовершенствования сводится на нет
стоимостью его реализации. В моем случае это была, в основном, стоимость до-
полнительной нагрузки на инсталлятор и сам}' программу в связи с наличием
внешних файлов с данными: инсталлятору пришлось бы приложить дополни-
тельные усилия по корректной установке этих внешних файлов, а самой
программе - должным образом заботиться о проверке их доступности и цело-
стности. Всегда есть ненулевая вероятность того, что внешний файл может быть
удален, случайно подменен или модифицирован - вы никогда не можете де-
лать никаких определенных предположений о состоянии и судьбе «выставлен-
ных напоказ» данных, если ваша программа не следит за ними специально.

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

4. Разумно эксплуатируйте публичные исходные коды

Сегодня нас захлестывает настоящая приливная волна бесплатных, обще-
доступных исходных кодов книги, журналы, диски CD-ROM, конференции
Интернета, ftp-серверы и т. д. Мир качается на волнах океана С, сверкает в

огнях Pascal и кренится под тяжестью LISP. Ей Богу, если хорошенько поис-
кать, то, вероятно, можно найти версию Pong1 на COBOL!

Тут же встает вопрос: следует ли все это использовать?
Ответом является «да», но с серьезной поправкой. Поправка же состоит в
том, что вы должны делать это «разумно».

Прежде всего, поправка «разумно» означает, что вы должны судить о коде
только по его качеству (или по отсутствию такового). Вы вообще не должны
ни принимать, ни отвергать что-либо только лишь потому, что оно причислено
к какой-то категории (такой, как бесплатное программное обеспечение). Такой
подход неуместен в отношениях между людьми, он не годится и для программ.

Кроме того, «разумно» означает, что вы должны выработать для себя
хорошо продуманную, четкую процедуру для заимствования какого-либо кода,
пришедшего извне, в вашем проекте. В большинстве случаев вам абсолютно не-
известно, кто писал код гуру программирования или какой-нибудь не-
андерталец (не говоря уже о персональной идентификации автора). Очевидно,
вы должны проявлять чрезвычайную осторожность. Например, даже не ду-
майте использовать материал такого сорта в публичной программе, если у вас
нет всех исходных текстов. Тщательно проверьте и протестируйте все исход-
ники прежде, чем начнете использовать их в производственных условиях; ни в
коем случае не считайте код корректным только потому, что он успешно ском-
пилировался без особых усилий.

Некоторые программисты серьезно сопротивляются использованию чужих
исходных кодов, отгораживаясь от них Великой Китайской Стеной. А вы?
Знаете ли вы о программировании все, что надо знать о нем? Можете вы напи-
сать промышленного качества функции, которые будут кодировать методом
Хаффмана, выполнять быстрое преобразование Фурье, совершать цифровое
разложение BMP-файла, вычислять точную дату рождественского воскресенья
для заданного года? Лично я не могу. По крайней мере, без изрядных усилий
по исследованию предмета или без общедоступного кода.

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

5. Не влюбляйтесь в ваш новый блестящий молоток

Но ведь это так естественно, в самом деле! Вы принимаете решение пустить
инструмент в дело, ступаете на одну ступеньку вниз, на более низкий уровень
цикла исследования и эксплуатации, и, наконец, находите многочисленные По-
истине Крутые Штуковины всевозможных сортов, заложенные в этот продукт.
Вы еще этого не знаете, но вы уже влюбились в него без памяти: вы рассылаете
друзьям обширные электронные письма, полные бурных излияний ваших
чувств к этому новому, открытому вами Чудо-Прибамбасу. Своими рассказами
о нем у кофейного автомата, и во время обеденного перерыва вы до слез надое-
даете вашим коллегам. Пока это все нормально, хотя и немного стесняет
окружающих.

Опасно то, что происходит чуть позже: сладостное головокружение вызы-
вает некоторое брожение в мозгах и трансформируется в фанатизм, серьезно
деформирующий ваше мироощущение. С какого-то момента каждая проблема
кажется вам гвоздем, который только и жаждет, чтобы вы вогнали его по самую
шляпку ударом вашего нового блестящего молотка.

Лучшая защита от такой «любви» - тот самый маленький червячок в ва-
шей голове, о котором я упоминал выше. Вы должны все время быть настороже
и сопротивляться каждому малейшему проявлению такой привязанности к
инструменту, причем не только у самого себя, но и у других членов вашей
программистской команды.

Я должен сознаться, что сам достаточно склонен к такой влюбчивости, так
что обо всех связанных с этим опасностях я знаю из первых рук. Иногда
я с некоторым смущением вспоминаю тот эпизод из раннего периода моей
программистской карьеры, когда я впервые познакомился с теорией
конечных автоматов и с основанным на ней табличным методом обработки
данных, который использовался для реализации очень эффективных син-
таксических анализаторов. Я так влюбился в эту теорию, что пытался ис-
пользовать конечные автоматы для решения практически каждой
встречной проблемы. Моему менеджеру и моей жене порой приходилось
буквально держать меня на привязи, пока мое лихорадочное возбуждение
не прошло окончательно.

Особенно скверной является такая вариация этой темы, при которой основ-
ной акцент ставится на слово «новый» в метафоре с блестящим молотком.
Слишком часто свежеиспеченные новые инструменты и/или технологии во-
влекаются в программные проекты по абсолютно неверным причинам: из-за
безрассудного увлечения программиста, из-за благонамеренного, но основан-
ного на неверной информации приказа руководства, из-за слепого доверия к
поставщику или из-за какой-нибудь комбинации перечисленных выше ошибок.
Когда только возможно, вы должны сопротивляться любому навязыванию но-
вых инструментов и технологий до тех пор, пока эти новинки не докажут свою
готовность. В конце концов, мы работаем в той самой индустрии, в которой на
протяжении многих лет со сцены не сходила шутка о том, как безрассудно по-
купать версию 1.0 любого продукта. В течение последних нескольких лет эта
шутка (а если быть более точным - народная мудрость) слегка видоизмени-
лась, и теперь она гласит: «ни в коем случае не покупайте версию х.0 чего-
либо». И надо признать, что эта новая формулировка служит весьма метким,
хотя и грустным, комментарием о сегодняшнем состоянии дел в производстве
коммерческого программного обеспечения. К сожалению, эта мудрость в той же
степени применима и к программистскому инструментарию в широком смысле
слова.

Например, вспомните дебют технологии OLE. Журналы наперебой публи-
ковали минимальные демонстрационные OLE-программы, содержавшие
тысячи строк кода предназначенных только для поддержки OLE. Теперь же,
когда у нас есть библиотеки, практически полностью инкапсулирующие рутин-
ные элементы поддержки OLE, только какие-нибудь экстремальные условия
(или врожденный авантюризм) могут заставить программиста взяться за
«ручное кодирование» поддержки OLE и пойти тем самым на огромный риск
введения в продукт серьезных и трудноуловимых ошибок.

На мой взгляд, в обычных проектах не стоит торопиться с внедрением таких
внушительных архитектурных решении, как OLE (и даже менее устрашающих
новинок типа новых элементов управления Windows 95) до тех пор, пока не
появится хорошая инфраструктура (читайте: каркасная библиотека) их
поддержки. А в тех относительно редких случаях, когда вы не можете дожи-
даться такой подмоги, вы должны быть готовы к тому, что «официальная»
версия вспомогательной поддержки рано или поздно появится. И тогда вам
придется решать, останется ли ваша оригинальная реализация жить в виде дои-
сторического ископаемого, или она будет переделана с использованием нового
инструментария.
{mospagebreak}
Типы инструментов

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

Справочные материалы

Трудно иметь под рукой слишком много справочных материалов. Иначе
говоря, когда про этот особый тип инструмента спрашивают «Сколько еще
нужно?», на это почти всегда отвечают: «Больше!»

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

Например, всякий раз, когда вы вторгаетесь в какую-то область Windows-
программирования впервые или погружаетесь в нее глубже, чем это делали
раньше, вы должны потратить несколько минут на поиск тех API, которые вы
будете использовать в первый раз или которые вы хотите применять более ос-
новательно. Внимательно прочитайте всю информацию о них, доступную в
сопроводительной документации и на Microsoft Developer Network CD. Звучит
так, как будто я только что предложил вам провести время за странным
занятием за чтением словаря, не правда ли? Что ж, в каком-то смысле
именно это я и предложил. В справочниках, в этих своеобразных «словарях
АРI» есть масса интересной чепухи, которая окажется весьма полезной для вас.
(Кстати, если вы никогда не читали настоящий словарь, то я советую попробо-
вать и это. А тем любопытным, кому ваше поведение покажется странным, вы
всегда можете сказать, что занимаетесь сравнительным частотным анализом ла-
тинских и греческих словообразующих корней в рамках большущего прави-
тельственного заказа.)

Как только вы начинаете думать, что ваши новые знания об API достаточно
закреплены, нередко есть смысл потратить еще немного времени и усилий на
написание простых тестовых программ, на которых вы должны поупражняться
в использовании этих API и заодно убедиться, что они и в самом деле работают
так, как вы представляете. Вы спросите, зачем же вы должны тратить свое
драгоценное время на это занятие? Ответ прост: практически вся программист-
ская документация, с которой вы имеете дело (а относящаяся к Windows - в
особенности), крайне ужасна. Она неточна, в ней опущены критические де-
тали, в ней жутко не хватает ссылок на другие темы, и она никогда не дает вам
полной картины. (Кстати, именно тут хорошим подспорьем может оказаться
Windows/DOS Developer's Journal - ежемесячно они публикуют несколько
аннотаций к справочному файлу по SDK, которые латают дыры и исправляют
ошибки. И хотя эти усилия достойного журнала выглядят в какой-то степени
униженными самой проблемой, я настоятельно рекомендую вам пользоваться
этой помощью.)

На тот случай, если вы решите, что я дурачусь или чересчур осторожничаю
в отношении документации к API, вот вам примеры для размышлений:

В Один из самых больших сюрпризов в Windows 95 (но не в Win32s)
заключается в том, что все координаты, переданные при вызовах GDI-
функций, ужимаются с 32 бит до 16. То, что получится при таком сжа-
тии (а оно может привести не только к изменению абсолютного
значения, но и к смене знака), и будет использовано модулем GDI, а
вызывающая программа останется без какого-либо уведомления об
этой проблеме. В главе 14 на стр. 473 вы сможете найти более подроб-
ную информацию об этой аномалии, включая описание некоторых спо-
собов обойти ее.

В Некоторые «булевские» функции в Win32 в действительности
возвращают не TRUE, а непредсказуемые ненулевые значения. Это
может приводить к некоторым странным проблемам. Например, вот
такая проверка:

   if(GetClientRect(hWnd,lpRect) == TRUE)
     {/*... */ }

будет всегда оканчиваться неудачей, даже в том случае, когда вызов
функции GetClientRect() абсолютно успешен. А вы потратите не-
сколько часов на выяснение того, почему ваш код работает неправиль-
но, хотя проверка возвращаемого функцией значения производится в
точном соответствии с документацией. Несмотря на то, что многие
программисты на C/C++ обычно следуют общеизвестному соглашению
о трактовке любого ненулевого целого значения как TRUE (кстати, в
заголовочных файлах для Win32 константа TRUE строго определена
равной 1), вы никак не можете критиковать этот код за некоррект-
ность, поскольку он следует букве закона (то есть верховному
критерию правильности программы). Тем не менее, вне зависимости от
того, кто тут прав, а кто нет, возникшая ошибка может быть очень
дорогостоящей, особенно если пользователи наткнутся на нее раньше
вас.

Кроме того, это небрежное нарушение документации (или недокумен-
тированная небрежность?) может повлечь за собой целую вереницу
других, еще более неуловимых проблем. Что, если вы пишете функ-
цию, которая по определению должна возвращать значение типа
BOOL, и код которой после выполнения каких-то своих действий
вернет результат работы одного из тех самых причудливых «булев-
ских» Win32 API, которые так коварно пользуются вольной ин-
терпретацией TRUE? Это хороший, пусть и не самый опасный, пример
того, как плохие данные могут попасть в вашу программу и
распространиться по ней. В данной книге я говорил и еще неоднократ-
но повторю, что вы должны весьма скептически относиться к правиль-
ности данных, которые не создаются и не проверяются вашим собст-
венным кодом. Поэтому наиболее надежным (хоть и немного странным
на вид) решением в данном случае была бы замена

   return SomeWIN32API(/*...*/);

на

   return (SomeWIN32API(/*...*/) != FALSE):

которая позволит вашей функции строго соответствовать своему опи-
санию, несмотря на причуды Win32 API и его документации.

в Функция FindFirstFile() (которая может использоваться для трансля-
ции псевдонимов файлов в их длинные имена и обратно, а также для
поиска файлов) не принимает имена файлов с символом «обратная ко-
сая черта» на конце, но зато прекрасно понимает такие имена, в ко-
торых на местах символов «обратная косая черта» стоят символы
«прямая косая черта». Ни один из этих двух интересных фактов не за-
документирован. К тому же в этой функции есть документированная
ошибка при обработке метасимволов: символ «?» трактуется как
«любой символ», а не как «любой символ или ничего». (Подробности
вы можете найти в PSS-статъе Q130860 на Microsoft Development Net-
work CD.) Это пример еще одной проблемы, которая, подобно случаю
с "ложным TRUE", может вылиться в долгие отладочные сеансы.

В Функция GetLongPathName() (новый сервис INT 21h/7160h, появив-
шийся в Windows 95 и не имеющий эквивалента в Win32 API) примет
завершающий символ «обратная косая черта» в указанной строке с пу-
тем. Если бы при работе с файловой системой именно эта функция
встретилась вам первой, вы, наверное, сделали бы резонное предполо-
жение, что и другие новые функции этого «семейства» принимают и
игнорируют завершающие черточки. И вы оказались бы неправы.

В Наш старый добрый друг, функция _lореn() примет и корректно
обработает длинное имя файла, даже если вызвать ее из 16-разрядной
программы, в штампике "ожидаемая версия Windows" которой
проставлено 3.1. Но этот весьма удобный (для некоторых) факт, на-
сколько мне известно, никак не отражен ни в справочном HLP-файле,
ни где-либо еще. (Я сделал это открытие совершенно случайно, когда
экспериментировал с программой-примером LFNCD для главы 12 на
стр. 425. Подробные детали об этом вы можете найти во врезке «Ис-
пользование длинных имен файлов в 16-разрядных программах: да
здравствует алхимия!» на стр. 395.)

В Иногда функция GetShortPathName() работает по-разному в
Windows 95 и Windows NT. В главе 14 на стр. 486 я подробнее расска-
жу о ней и о том, как можно «обернуть» этот API для того, чтобы уни-
фицировать его поведение на различных платформах Windows.

Но это все частности. А обобщенную мораль я сформулировал бы так: для
вас является жизненно необходимым всегда оставаться студентом програм-
мирования. И для Windows-программистов это важно более чем для кого-либо
другого. Под жестоким натиском новых API и возможностей, мы все чувствуем
себя, как новичок на велотренажере - неистово крутим педали, спидометр по-
казывает 100 миль в час, а на самом деле мы остаемся на месте (я мог бы порас-
суждать насчет того, кто в данном случае выступает в роли тренера - сидит
рядышком с секундомером в руках и наслаждается зрелищем, но благоразум-
нее будет промолчать). Все это чертовски изнурительно, но на самом деле у нас
нет выбора. Стоит вам, профессиональному программисту, один раз отстать, и
потом будет очень трудно найти силы, чтобы догнать уходящий поезд.

Невзирая на ограниченность времени, которое отводится вам на образова-
ние, вы должны знакомиться с другими областями программирования,
отличными от той, в которой вы обычно работаете. И совсем не обязательно
программировать в них, достаточно хотя бы читать какую-нибудь литературу.
Например, я давно убежден, что всем кодировщикам следует читать о
программировании игр - как о проблемах искусственного интеллекта (в таких
играх, как шахматы и шашки), так и о вопросах реализации интенсивной ани-
мационной графики (вы угадали: отличным примером тут является знаменитый
Doom). Почему? Дело в том, что программирование игр - чертовски сложная
область, и вы можете почерпнуть оттуда массу полезных знаний и технологий,
пригодных для использования в вашей собственной работе (самый простой
пример в этом случае - различные приемы оптимизации).

Иногда все программистское сообщество представляется мне группой
спортсменов на соревнованиях по бегу с препятствиями. Вот появилась
очередная крошечная помеха, через которую нужно перепрыгнуть (новая
версия DOS); затем показался довольно высокий забор, через который
нужно перелезть (переход от DOS к событийно-управляемой Windows);
а теперь перед нами - огромная стена (OLE, Win32, сетевое
программирование и все прочие важные новинки и изменения середины
90-х). Почти на каждом препятствии часть бегунов сходит с дистанции от
истощения (иногда скорее от ментального, чем от физического) или нахо-
дят себе какую-нибудь нишу, в которой продолжают' бег (но уже трус-
цой). Некоторые постоянно рвутся вперед, чтобы любой ценой занять
место во главе всей своры. А тем временем большинство из нас
продолжают бежать круг за кругом, не переставая удивляться тому, что
на каждом кругу встречаются новые препятствия, порой так похожие на
предыдущие.

Компиляторы и языки программирования

О'кей, вот и пришло время для хорошенькой, старомодной религиозной
войны. Честно говоря, начиная эту книгу, я не был уверен, стоит ли касаться
пресловутой темы «C/C++ против Pascal»; у меня было сильнейшее искушение
вообще игнорировать ее. При том плачевном положении Pascal на рынке
средств разработки для Windows, которое он занимал в начале 1995 года, вряд
ли имело смысл даже упоминать этот язык. К тому времени война языков почти
закончилась, было совершенно ясно, что C/C++ и Visual Basic победили, a Pas-
cal неслышно скользил за линию горизонта, как раз в направлении побережья
Scotts Valley. Трудное финансовое положение Borland было широкоизвестно,
что также играло определенную роль во всем процессе.

А затем, уже по дороге на похороны, случилась забавная вещь: Borland вы-
пустила Delphi.

Возможно это и не показалось бы вам столь забавным, окажись вы тогда
на месте Microsoft, успешно продававшей Visual Basic, или на месте любого по-
ставщика C/C++, потому что после этого события в течение первого же
квартала было продано 125 ООО копий Delphi. Эти продажи внесли значитель-
ный вклад в показатели Borland на конец квартала, и 30-го июня компания
преподнесла аналитикам (да и почти всем на планете Земля) огромный
сюрприз - показала наличие прибыли.

Что же произошло?

Конечно, это была классическая встреча спроса и предложения. Я думаю,
что Delphi очень вовремя (для Borland) удовлетворили отчаянную нужду
значительной части программистского сообщества, но нужду не в Pascal как та-
ковом, а в чем-нибудь, отличном от C/C++. На основании моих личных бесед
со многими программистами, я пришел к убеждению, что многих из них просто
тошнит от причудливых лабиринтов и путаниц C/C++ (а также сопутствующих
ему библиотек и интегрированных сред разработки), и что они всего лишь хо-
тят видеть рациональную альтернативу.

В апрельском выпуске Software Development Jornal за 1995 год я опубли-
ковал статью о возможностях повторного использования кода в Delphi - об
одной из сильнейших характеристик этого продукта. Эта статья вызвала целый
поток писем в мой адрес, поразительно похожих друг на друга. Почти все со-
общения были от корпоративных разработчиков, собиравшихся начинать свои
новые, в некоторых случаях довольно крупные, проекты, и просивших моего
совета - не лучше ли им оставить C/C++ и перейти на Delphi. Основываясь
на информации об их проектах, которую они мне предоставили, я дал им
соответствующие рекомендации. Но один мой совет был одинаковым для
всех - приобрести копию Delphi и оценить продукт самостоятельно. (На мой
взгляд, глубоко ироничным был тот факт, что еще до выхода финальной
версии Delphi этому продукту был присвоен титул «Visual Basic killer», тогда
как ни в одном из вышеупомянутых писем Visual Basic даже не упоминался в
качестве возможного варианта для более-менее серьезного проекта. Везде
выбор делался между C/C++ и Delphi, и лишь один респондент спрашивал про
PowerBuilder. Я полагаю, Delphi кладут Visual Basic на обе лопатки. Но мне
кажется, что какая-то еще большая игра лишь только затевается.)

Бывалые ветераны «языковых войн» часто отмечают, что следует четко
отличать языки от компиляторов. Это правильная точка зрения, но только до
тех пор, пока вы ведете лишь теоретические споры об инструментах. Реальное
положение дел на этом рынке таково, что на самом деле вы выбираете не язык,
а прежде всего продукт, который, в свою очередь, уже включает в себя какой-
то язык. Кстати, в отношении Pascal этот нюанс практически незаметен - на
сегодняшний день Delphi от Borland, включая каркасную библиотеку VCL, по
сути безраздельно господствует на всем рынке «паскалевских» инструментов
разработки для Windows. А с языком С++ ситуация намного сложнее, так как
существует несколько приблизительно равномощных компиляторов, раз-
нообразные каркасные библиотеки и т. д. - все это предоставляет достаточно
много степеней свободы для выбора и смешения.

Ну, ладно, достаточно я увиливал от прямого ответа. Теперь я готов выйти
вперед и заявить, что я отдаю решительное предпочтение Pascal перед С++ для
практически любого программного проекта. И именно Pascal я использую в
своих контрактных работах когда только возможно. Используя Pascal, я могу
создавать надежные, высококачественные, легко сопровождаемые программы
гораздо быстрее, чем при помощи любого инструментария, ориентированного
на С++. И в этой книге я использовал для примеров Visual С++ только потому,
что сегодня Visual С++ является, доминирующим инструментом разработки для
Windows, и, следовательно, именно код на С++ является наиболее доступным.

C/C++: системный язык, используемый в качестве прикладного

C/C++ появился на свет в начале 70-х и предназначался для системного
программирования. Поэтому вовсе не удивительно, что язык С (а в еще боль-
шей степени - сам факт построения С++ на основе С) демонстрирует филосо-
фию, подходившую для этой роли и для того времени. Сегодня мы используем
эти языки для прикладного программирования и тем. самым эксплуатируем их
такими способами, которых создатели и не представляли.

Классическим примером является схема работы с. заголовочными файлами.
В старые добрые времена заголовочные файлы были маленькими, и никаких
особых проблем не возникало. По крайней мере, с точки зрения производитель-
ности. Сегодня это не так. Например, Visual С++ предоставляет вам следующие
заголовочные файлы:

\MSVC20\INCLUDE: 247 заголовочных файлов, 4.4 Мб
\MSVC20\INCLUDE\SYS: 5 заголовочных файлов, 6 Кб
\MSVC20\INCLUDE\GL: 3 заголовочных файла, 84 Кб
\MSVC20\MFC\INCLUDE: 44 заголовочных файла, 604 Кб

Конечно, в отдельно взятом проекте вам вряд ли понадобятся все эти фай-
лы. Но те заголовочные файлы, которые необходимы для Windows-програм-
мирования, в сумме представляют собой сотни килобайт, что при компиляции
вызывает серьезные проблемы с производительностью.

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

Блиц-вопрос: в нем отличие между «const char *х» и «char const *х»?
Первое - это указатель на постоянный символ, а второе - постоянный
указатель на символ. Многие программисты, пишущие на С++, легко
ошибутся при ответе на этот вопрос, если только не задать его в контексте
конкретного исходного кода. Либо они будут вынуждены достаточно
долго думать над ответом, что в большинстве случаев все равно приведет
к ошибке.

Компиляторы C/C++ не обеспечивают программисту достаточный уровень
помощи. Например, Visual С++ 2.2 не выдаст и предупреждения (даже при
включении максимального уровня предупреждений), встретив такую строку:

int fred[100] = { 10, 10 };

Некоторые апологеты C/C++ заявляют, что это хорошо («Это не баг, это
фича!» [it's not a bug, it's a feature!]); поведение компилятора в такой
ситуации четко определено и дает программисту изящный, быстрый способ
проинициализировать первые несколько элементов массива, а остальные оставить
 компилятору, который автоматически присвоит им всем известное и полезное значение -
 ноль. А теперь задумайтесь о том, в какой кошмар это может вылиться при сопровождении
 С-кода. Например, пусть в вашем большом: проекте есть несколько массивов оди-
накового размера (заданного одной константой), полностью инициализирован-
ных строками следующего вида:

int fred[fred_size] = { 10, 10 };

Пусть в какой-то момент вам потребовалось изменить размеры всех этих
массивов с двух до четырех. После того, как вы измените значение константы
fred_size, вам придется проверить каждое место, где она использовалась для
инициализации массивов. И делать это вам придется «врукопашную», посколь-
ку компилятор не предоставит вам никакой помощи в этой ситуации. Для срав-
нения: в Delphi, в аналогичном случае несоответствие количества инициали-
зированных элементов размеру массива считается ошибкой, поэтому места для
ваших ошибок тут уже не остается.

Продолжая разговор о массивах, я просто поражаюсь, почему C/C++ до сих
пор не поддерживает массивы с ненулевым началом отсчета индексов? Вам нужен
массив с индексами в диапазоне от -37 до 104? В Delphi вы можете просто объя-
вить его именно таким, и нет проблем. А C/C++ вынудит вас заниматься арифме-
тикой индексов самостоятельно. Еще более поразительным является наличие (оп-
циональное!) в Delphi такой возможности, как автоматическая проверка указателя
на элемент массива на предмет выхода его за допустимые границы (хотя Pascal
нуждается в таком контроле значительно меньше, чем C/C++).

Все же в С++ есть один способ преодолеть многие проблемы, связанные с
массивами: написать свой собственный класс, эквивалентный встроенной реа-
лизации массивов, и добавить в него все недостающие возможности - произ-
вольные диапазоны индексов, пограничные проверки и так далее. Однако это
довольно смехотворный аргумент - он лишь подтверждает мое давнее убежде-
ние, что в контексте прикладного программирования С++ скорее является
очень полным языковым снаряжением, а не полнофункциональным языком
прикладного программирования.

В чем отличие между языком, который по сути является лишь «языковым
снаряжением», от истинного прикладного языка? Ответ прост: в общей
направленности реального применения базовых возможностей по соз-
данию повторно используемых компонентов. В первом случае они очень
часто используются для «программирования вниз» - добавления таких
вещей, как пограничных проверок индексов массивов, строк переменной
длины и т. п. - то есть для создания таких возможностей, которые
должны быть встроены в сам базовый язык. Во втором же случае язык
используется практически только для «программирования вверх» - для
создания компонентов прикладного уровня (например, для реализации
специального вида диалога About, который затем может быть легко
встроен во многие программы и проекты).

И это были всего лишь несколько мелких примеров... (Правда, в главе 3
я также касался одной из подобных проблем - причудливого мира печально
известных «строк, завершаемых нулями».) К огорчению всех пользователей
С++, язык действительно перегружен подобными недостатками, и из-за этого
все известные мне реализации этого языка являются поистине враждебными по
отношению к программисту. В качестве последнего примера, позвольте мне за-
дать один вопрос: вы замечали, как порой сходят с ума компиляторы C/C++
от пропущенного символа «точка с запятой»? Если вы уберете этот символ у
последнего прототипа функции в заголовочном файле, то получите целое по-
лотнище сообщений об ошибках, ссылающихся на строки файла реализации (в
который был включен «искалеченный» заголовочный файл) и сбивающих вас
с пути. Выбросите этот символ в Delphi - и компилятор наведет курсор точно
на цель и скажет вам, что в этом месте он ожидал увидеть именно точку с за-
пятой.

Некоторые могут возразить мне, что поведение компилятора C/C++ сильно
варьируется от одной реализации к другой, и что производители этих компи-
ляторов делают буквально все, что могут, пытаясь сделать свои инструменты
удобнее и эффективнее. Вот в этом-то и состоит проблема: базовый язык на-
столько устарел, что для использования его в разработке приложений для Win-
dows даже таких титанических усилий оказывается недостаточно. Мне меньше
всего хотелось бы осыпать ту или другую сторону обвинениями по поводу этих
проблем. И вам не следует этого делать. Значение имеют лишь объективные,
текущие и перспективные характеристики тех инструментов, которые мы ис-
пользуем.

Миф о переносимости

Годами переносимость кода преподносилась как одна из сильных сторон
языка С. Позже эту характерную черту стали приписывать и его славному
потомку - языку С++. Наверное, это сделали просто по инерции, потому что
переносимость кода, написанного на С++ - смехотворный миф, особенно если
вести речь о Windows-программировании.

Для примера, возьмите исходный код программы WordPad, написанный
для Visual С++ 2.2 и соответствующей версии MFC, и попробуйте перенести
его в Borland С++, который не поддерживает MFC. Думаете это нечестный
пример? Тогда попробуйте различия в реализации шаблонов, обработки
исключительных ситуаций, других мелких возможностей языка. Благодаря
многообразию каркасных библиотек и тому факту, что стандарта языка С++ до
сих пор нет, все ныне существующие пакеты для Windows-разработки на C/C++
на самом деле являются разными диалектами одного и того же языка (а в не-
которых случаях - вообще разными языками).

С другой стороны, Object Pascal от Borland был безумно нестандартным
изначально и будет таковым всегда. Но он имеет при этом одно подавляющее
преимущество - он один на свете. Поэтому перенос кода, написанного на Ob-
ject Pascal, с одного компьютера на другой никогда не является проблемой.

Предназначение такой вещи, как стандарт языка программирования,
заключается в том, чтобы все отвечающие ему реализации могли быть дос-
таточно схожими, и чтобы перенос кода с одной реализации на другую мог осу-
ществляться эффективно и приводил к предсказуемым, успешным результатам.
Как же иронично выглядит тот факт, что именно нестандартный язык Delphi
являет собой окончательное решение проблемы переносимости: поскольку су-
ществует только одна его реализация, все ее инсталляции по определению
идентичны (а не только «достаточно схожи»).

C/C++ и определяемые пользователем типы

На мой взгляд, наиболее странной чертой С++ является его одержимость
следующей идеей: дать возможность вашим собственным классам выглядеть по-
хожими на встроенные типы данных. Один только этот замысел потребовал
массу других возможностей и сделал язык более сложным, более медленным,
в смысле компиляции, и, в конечном итоге, - более чреватым ошибками. Не-
явные вызовы конструкторов и деструкторов, конструкторы копирования,
переопределение операторов, ссылки - вот неполный перечень самых неисся-
каемых источников трудноуловимых ошибок.

Object Pascal, для сравнения, имеет настолько упрощенные возможности
ООП, что программисты бывают шокированы при знакомстве с ними после
С++. Нет множественного наследования? Нет переопределяемых функций и
операторов? Нет функций, возвращающих ссылки? Как же все это может быть
серьезным языком для ООП? Это случайно не ООП для «чайников»? На са-
мом же деле оказывается, что эти вещи не нужны в качестве внутренних язы-
ковых возможностей, даже если потом приходится строить их логические экви-
валенты другими средствами.

Например, программисты на С++ очень часто реализуют канонический
конструктор копирования, который дает возможность делать «глубинные» ко-
пии объектов. Если такого конструктора в классе нет, то С++ будет совершать
побитовое копирование объекта. И если среди членов класса есть хотя бы один
указатель, такое «поверхностное» копирование может привести к проблемам
самого разного сорта. (Наиболее типичный пример: если значение такого ука-
зателя получено путем динамического размещения памяти, то после побитового
копирования исходного объекта у вас появится еще один объект, содержащий
указатель на тот же самый блок памяти; и если теперь какой-нибудь из этих
объектов попытается освободить эту память после того, как это уже сделал
другой объект - серьезные неприятности вам гарантированы.) В Object Pascal
есть ровно такая же проблема. И функция «глубинного» копирования может
понадобиться в обоих языках. В Object Pascal использование такой функции
может выглядеть примерно так:

  DeepCopyFred(destFred,sourceFred);

а на С++ так:

  destFred = sourceFred;

Надо отдать должное языку С++: его дополнительные возможности позво-
ляют не только делать одностроковые выражения более элегантными с виду.
Нельзя отрицать, что поддержка объектов в С++ глубже и полнее, чем в Object
Pascal. Это очевидно является выигрышем, поскольку дает возможность ис-
пользовать созданные объекты более обобщенно и естественно. Но какой ценой
это достигается? Действительно ли повышенная сложность и запутанность язы-
ка является разумной ценой за более полные возможности эксплуатации объ-
ектов? Или все-таки эти дополнительные возможности являются лишь «теп-
личными» и непригодными для использования в реальном мире? Мой опыт,
основанный на многих тысячах строк Windows-кода, подсказывает, что верным
является последнее.

Аттестации, аттестации и еще раз аттестации

Модули языка Pascal являются логическими эквивалентами комбинации
прекомпилированных заголовков и OBJ-файлов языка С++: они имеют
примерно такие же размеры, но их построение не сопровождается такими
неприятными накладными расходами на начальную компиляцию. Это одна из
основных причин, но которым Delphi компилируют значительно быстрее ком-
пиляторов C/C++. С какой скоростью компилируют Delphi? В качестве ответа
на этот вопрос я предлагаю вам взглянуть на результаты небольшого сравни-
тельного эксперимента, который я провел на своей системе с процессором 486-
DX4-100 и 32 Мб оперативной памяти, разумеется, под управлением Windows
95. Для эксперимента я выбрал компиляторы Delphi и Visual С++ 2.2 и исход-
ные тексты Stickles! и WordPad (последние входят в поставку Visual С++ 2.2).
Я изменил настройки проекта WordPad таким образом, чтобы сделать компи-
ляции в Delphi и Visual С++ как можно более близкими друг другу в смысле
затрат: я отключил все оптимизации (поскольку Delphi не являются оптими-
зирующим компилятором), использовал статическую версию MFC (поскольку
версия OWL для Pascal может быть скомпонована только статически), а также
выключил генерацию М АР-файла и базы данных для броузера. Вот сводка ито-
гов моего эксперимента:

Stickies!

16-разрядный Borland Pascal/OWL
43 696 строк исходного кода
48 единиц компиляции
32 ресурсных файла, суммарный размер - 146 Кб
Суммарное время компиляции и компоновки: 16 секунд


WordPad

32-разрядный C++/MFC
14 392 строки исходного кода (11 421 строка С++ и 2971 строка заголовочного файла)
36 единиц компиляции
24 ресурсных файла, суммарный размер результирующего RES-файла - 54 Кб
Суммарное время компиляции и компоновки: 5 минут 7 секунд
Компиляция ресурсов - 10 секунд
Компиляция STDAFX.H - 53 секунды
Компиляция исходников - 3 минуты 28 секунд
Компоновка - 36 секунд

Пуристы от тестирования, наверное, протестующе взвоют от сравнения 16-
разрядного компилятора с 32-разрядным, смешения языков, исходных данных
и вообще почти всего в одном тесте. Такой протест был бы вполне обоснован-
ным, если бы я, скажем, пытался сравнивать влияние какого-то отдельного
компонента (языка или каркасной библиотеки) на общую результирующую
производительность. Но меня волнует как раз не это. Моя главная цель в дан-
ном эксперименте - дать вам почувствовать разницу в быстродействии этих
двух сред разработки на реальных проектах. Как показывают приведенные
выше цифры, Delphi и Visual С++ даже близко не стоят друг к другу. По ко-
личеству строк кода WordPad втрое меньше Stickies!, в то время как полное
его построение происходит в 19 раз дольше.

Если вы возразите, что полное построение приложения - это еще не все,
и что вы гораздо чаще выполняете лишь частичные сборки проекта, я не буду
спорить. Я просто сообщу вам. результаты еще одного дополнительного
эксперимента с теми же инструментами: выбрав в исходниках Stickiest и Word-
Pad примерно одинаковые, маленькие единицы компиляции (около 250 строк
кода в каждой) и сделав в них тривиальные изменения, я пустил секундомер и
«mаkе». Результаты: Delphi/Stickles! - 5 секунд, Visual C++/WordPad - 35
секунд. Что ж, по крайней мере порядок превосходства уменьшился на еди-
ницу. Правда, при этом можно заметить другой интересный факт: частичная
сборка
в Visual С++ 2.2 (35 секунд) все же происходила в два раза дольше,
чем полная сборка в Delphi (16 секунд).

Кто-то скажет вам, что разница в производительности операций такого
сорта не имеет никакого значения в реальном мире, потому что в больших,
серьезных проектах основная часть вашего времени тратится на проектирова-
ние и тестирование, а не на наблюдения за мерцающим курсором во время ком-
пиляций и компоновок. В какой-то степени, это очень ценное замечание. Но, в
то же время, всякий, кто использовал С++ в большом проекте, также может
рассказать вам, насколько тягостными могут стать процедуры компиляции, и
как это деформирует рабочие привычки. Например, вы быстро заметите, что
стараетесь накапливать разнообразные мелкие изменения, которые нужно сде-
лать, а потом совершать их все за один раз, чтобы избежать нескольких лиш-
них компиляций. Я сам все время так делаю, работая на С++. Для сравнения,
при работе на Delphi я компилирую гораздо чаще просто потому, что там эта
процедура такая скоростная и дешевая. По сути, я использую компиляцию в
качестве средства быстрой проверки синтаксиса.

Чтобы зря не тратить силы на эксперименты с реальными проектами,
попробуйте экстраполировать результаты моего маленького теста. Даже если
великодушно предположить, что в большом проекте этапы компиляции
ресурсов, компиляции заголовков и компоновки займут столько же времени, а
время компиляции главного исходного кода будет линейно зависеть от его
размера, то для полной сборки проекта из 50,000 строк понадобится около 14
минут, а для 100,000 строк - около 26 минут. Я видел сборки намного более
длительные, происходившие на быстрых компьютерах и имевшие в распоряже-
нии больше ресурсов (оперативной памяти, скорости жесткого диска и свобод-
ного места на нем).

Еще один пример того, как скорострельность инструмента может влиять на
разработку: вы когда-нибудь пробовали изменить имя константы или перемен-
ной, широко используемой в проекте? Какая морока - выискивать все эти
ссылки редактором или шерстить исходники утилитой grep. В Delphi я просто
изменяю имя в месте его главного определения, а затем делаю повторные ком-
пиляции, по несколько секунд каждая. При встрече с каждой ссылкой, ставшей
теперь неизвестным именем, Delphi останавливаются и помещают курсор прямо
в нужное место - простой трюк, который все еще невозможен в Visual С++.

И чтобы не забыть...

До сих пор я упомянул .тишь некоторые из тex палок, которые C/C++ и
текущие его реализации вставляют в колеса программных проектов. Вот что
еще дают нам эти же самые инструменты:

В Чувствительность к регистру символов, которая порождает целый
класс Дурацких Синтаксических Ошибок, а также бессчетное ко-
личество семантически бессмысленных объявлений типа

  HWND hwnd;

В Условный оператор «?», который обычно генерирует точно такой же
код, как и классическая конструкция if/then/else, и вдобавок понижа-
ет читабельность исходного кода. Иными словами, он дает Вам отрица-
тельную прибыль (потерю читабельности) с нулевого дохода (произ-
водительность остается прежней) - любой дилетант в экономике ска-
жет вам, что это плохой бизнес.

В Сосуществование массивов, указателей и ссылок. Именно эта харак-
терная черта C/C++, как никакая другая, разоблачает его врожденную
ориентацию на системное программирование и вытекающую из этого
врожденную неуклюжесть в качестве языка прикладного програм-
мирования. Вы когда-нибудь пробовали обучать программистов-но-
вичков этой части языка? Я пробовал и могу без преувеличения ска-
зать: достаточно одно го-единственного урока на эту тему, чтобы убе-
дить кого угодно в контр-интуитивности этой области синтаксиса.

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

В Целочисленные типы, которые меняют свои размеры при изменении
целевого исполняемого кода с 16-разрядного на 32-разрядный. Вы мо-
жете предъявить более безумное архитектурное решение, чем это? (К
сожалению, Borland совершил ту же самую ошибку в 32-разрядной
версии Delphi - смотрите врезку «Delphi32: Pascal достигает со-
вершеннолетия» на стр. 190.)

В Бесконечные опции компиляторов и компоновщиков. Я до сих пор
качаю головой от недоумения по поводу того, как легко было создать
в Borland С++ 3.1 проект, который компилировался без каких-либо
неприятностей и собирал программу, моментально вызывавшую GPF
при запуске. Проблема (и ее решение) во всех этих случаях крылась
в настройках сборки; в некоторых случаях мне удавалось добиться тех
же самых проблем и с программами-примерами от Borland -■ просто
путем изменения опций.

В Отсутствие локальных функций (эту тему я подниму в главе 5).

В Компиляторы, щедро изливающие потоки неправильных или неуме-
стных диагностических сообщений.
Если бы компилятор не тратил
так много времени на сборку проекта порядочного размера, я бы
попросил его останавливаться после первой же найденной ошибки. Но
поскольку компилятор не так проворен, мне приходится за один
проход находить и исправлять все ошибки, какие возможно. Это оз-
начает для нас выбор - либо тратить массу времени на компиляции,
либо подолгу таращиться на целые диагностические «романы», пыта-
ясь отфильтровать ложные сообщения. (Поначалу я хотел объявить
конкурс на самое большое количество неповторяющихся диагно-
стических сообщений, которого можно добиться в C/C++ «односим-
вольной проблемой». Но потом я отказался от этой идеи - слишком
депрессивным было бы зрелище показательных выступлений кон-
курсантов.)

Пока преданные сторонники C/C++ не сформировали суд Линча и не
начали штурмовать Эндвелл, я поспешу сделать одну оговорку: ничто из вы-
шесказанного ни в коем случае не означает, что вы не можете писать перво-
классные прикладные программы на C/C++. Было бы абсурдом утверждать это
хотя бы: потому, что рынок программного обеспечения тут же предъявит сотни
контрпримеров. Суть моей точки зрения состоит лишь в том, что C/C++ возла-
гает на программистов гораздо более тяжелую ношу и требует от нас гораздо
больше стараний для уклонения от целых классов ошибок, которые при ис-
пользовании других инструментов либо вообще невозможно сделать, либо сде-
лать значительно труднее. Судя по качеству коммерческого программного обес-
печения для Windows, такие старания (или адекватные усилия по
тестированию, или и то и другое вместе) сегодня явно в дефиците.

Можно взглянуть на это и по-другому: несомненно, вы смогли бы писать
отличные Windows-приложения и на ассемблере, если бы захотели. Но в том-
то и дело, что никто на свете никогда не стал бы рассматривать такой вариант
в качестве разумного пути (разве что не в каких-нибудь экстремальных усло-
виях). Суть альтернативы в том, где именно пролегает пограничная линия ме-
жду «разумными» и «неразумными» программными инструментами в конкрет-
ных условиях и в конкретной задаче. По моему личному опыту, C/C++ и
сопутствующие ему инструменты почти всегда оказываются на той же стороне
этой границы, что и ассемблерный язык.
{mospagebreak}
Если Pascal тaк прекрасен...

...а C/C++ так ужасен, то почему Pascal не используют все? Это справедливый
вопрос.

Есть много причин, по которым Pascal не занимает сегодня достойного по-
ложения. Но первой на ум приходит серьезная проблема с имиждем, который
издавна закрепился за продуктами Borland, базирующимися на Pascal. Разве
можно было серьезно воспринимать компилятор, в названии которого стояло
слово «Turbo», а цена которого не составляла сотен долларов? Слишком мно-
гие разработчики, обращая внимание на подобную чепуху, совершили в те дав-
ние времена ошибку, отвергнув Turbo Pascal как «игрушку». Но так или иначе,
«игрушечная» репутация сохранилась за продуктами Borland.

Тем не менее, Pascal-продукты от Borland действительно имели и имеют
серьезные ограничения, такие как отсутствие поддержки различных моделей
пямяти (хотя в моей реальной работе это почти ни разу не было помехой) и
неспособность генерировать объектные модули в стандартном формате (что,
конечно же, является более серьезным недостатком).

Но вернемся к порокам Object Pascal. Канонический упрек критиков в
адрес всех разновидностей Pascal состоит в том, что он якобы представляет со-
бой «смирительную рубашку» и обычно не дает программистам достаточной
свободы при кодировании. Я был бы счастлив разгадать, откуда могло поя-
виться такое мнение (может быть, оно как-то связано с НЛО?), потому что в
действительности все совсем не так. Если вас достает проверка типов в Object
Pascal, вы делаете ровно то же самое, что и в другом строго-типизированном
языке под названием C/C++ - приведение типа. На самом деле, во всех моих
разработках я гораздо реже прибегаю к приведению типа в C/C++, чем в Pas-
cal. Среди 48 000+ строк на Pascal, которые составляют Stickies!, есть всего
дюжина приведений типа и только одна вариантная запись (конструкция, ана-
логичная объединению в языке С). И это - учитывая все те рукопашные бои,
которые Stickiest ведет с Windows API и OWL. Нет, лично мне это отнюдь не
кажется работой в «смирительной рубашке».

Другая, на этот раз действительно существенная, проблема заключается в
том, что Pascal-продукты от Borland всегда были слабоваты в поддержке проек-
тов и управлении ими. Borland Pascal и Delphi позволяют вам задать все опции
компиляции прямо из интегрированной среды, но не дают возможности
сохранять поименованные наборы этих настроек для проекта в целом (подобно
тому, как обстоит дело с конфигурациями проекта «Release» и «Debug» в Vis-
ual С++, к которым давно привыкли программирующие на C/C++). В Delphi
вам приходится заводить несколько отдельных проектов, включающих в себя
одни и те же исходные файлы - глупая и ненужная морока.

Единственной моей огромной и категорической претензией в адрес Pascal-
продуктов от Borland всегда была неудовлетворенность тем, что они сигна-
лизируют об ошибках, но не дают предупреждений. Я прекрасно понимаю, что
это одна из причин их поразительного быстродействия при компиляции, но мне
кажется, что обязательно должен быть обеспечен и компромисс, при котором
компилятор мог бы опционально обнаруживать и потенциальные проблемы, та-
кие как возвращение функцией указателя иа локальную переменную, заверше-
ние функции без явной установки возвращаемого значения, неиспользуемые
локальные переменные и т. д. и т. п. Для языка, задуманного как инструмент
обучения и широко используемого сегодня для разработки приложений, такие
недостатки являются серьезнейшими ограничениями. (Немного о том, как к
этим проблемам относится сама компания Borland, вы можете узнать из врезки
про Delphi32.)

Как жить с С++?

C/C++ не исчезнет на следующее утро. Да и вообще никогда не исчезнет,
коли на то пошло. Он уже так глубоко окопался, что все мы будем жить и рабо-
тать с ним, в той или другой форме, на протяжении десятилетий. Как же лучше
всего делать это?

Как я упоминал выше, когда вы выбираете инструмент, процесс изучения
и эксплуатации не кончается, а продолжается на микроуровне, в границах
выбранного инструмента. Вероятно, для компиляторов C/C++ это характерно
более, чем для большинства других инструментов. (Посмотрите врезку «Опти-
мизация: бесплатный сыр подан!» на стр. 182, и вы прослушаете поучительную
лекцию об одном из наиболее обманчивых свойств компиляторов C/C++.)

Во-первых, вы должны удостовериться в том, что до конца понимаете воз-
можности C/C++ и вашего компилятора: что они могут сделать для вашего
проекта, и чего не могут. Существует много хороших общедоступных книг о
языке С++, и в главе 16 я даже предложу вам конкретные рекомендации. Ос-
мотрите полки вашего местного книжного магазина, найдите две-три книжки,
которые покажутся вам наиболее подходящими, купите их, а затем прочтите их
полностью, от корки до корки. Вы будете поражены множеством интересней-
ших деталей, которые почерпнете из них.

Во-вторых, решите для себя, насколько глубоко вам необходимо
погрузиться в язык. Бьярн Страуструп, отец-основатель C/C++, в своей
превосходной книге The Design and Evolution of С++ говорит следующее:

По моему опыту, наиболее безопасный путь - изучать С++ снизу
вверх, то есть вначале изучить те возможности, которые С++
предлагает для традиционного процедурного программирования (т.
н. «улучшенный С»), затем научиться использовать и понимать
средства абстрагирования данных, а потом освоить использование
классовых иерархий для организации наборов взаимосвязанных
классов.


Я полностью согласен с вышесказанным, но хотел бы добавить к этому еще
одну деталь: при изучении этого языка вы не должны испытывать ни малейше-
го беспокойства, если вдруг обнаружите, что исследуете намного больше воз-
можностей, чем эксплуатируете. Например, я называю это «стратегией С++/
минус» и все время использую это в моих коммерческих проектах. В частности,
программируя на С++ для Windows; я использую тот самый диалект «С++ как
улучшенный С» - применяю MFC в качестве набора ячеек, в которые встав-
ляется мой код. В противоположность этому, мой код на Object Pascal выгля-
дит как взрыв на фабрике объектов - а все потому, что я чувствую себя
значительно комфортнее с объектной моделью Pascal, которая по количеству
сюрпризов для программиста даже близко не стоит к С++. Например, в Stick-
les! я использую десятки объектов с огромной пользой для программы.

Такая стратегия применения возможностей С++ годится для меня и для
моих проектов. Что годится для вас - решать вам. Но, собираясь принять
решение, обязательно помните, что нет такого правила, которое заставляло бы
вас задействовать все возможности языка или другого инструмента.

Программа Stickles! является хорошей иллюстрацией к рекомендации
«You(now) != You(later)», о которой я говорил в главе 2 на стр. 79.
Первоначальную версию я начинал писать в начале 1992 года, во времена
моего сильного (я бы далее сказал, почти маниакального) увлечения
ООП. В результате самый старый код в Stickles! оказался более объ-
ектно-ориентированным, чем если бы я писал его сегодня. Код, который
я добавлял для следующих версий, был уже не таким объектно-
ориентированным, поэтому в нынешних исходниках смесь стилей
кодирования и дизайна бросается в глаза настолько сильно, что я порой
сам не верю, что все это было написано одним человеком - мной. Так же
как и у вас, у меня вечно нет времени переписывать уже отлаженный и
распространяемый пользователям рабочий код только лишь для того,
чтобы привести его стиль к общему знаменателю. Поэтому Stickles! оста-
нется в своем нынешнем, слегка беспорядочном состоянии до тех пор,
пока не перепишу эту программу целиком по какой-то другой причине.

Каркасные библиотеки

Ваш выбор каркасной библиотеки (начиная с вопроса, выбирать ли что-то
вообще) так же важен, как выбор языка программирования. Он во многом
определяет, что вы сможете делать (или, по крайней мере, что вы сможете де-
лать легко).

Уверен, многие из вас сделали из вышесказанного вывод о том, что неис-
пользование каркасной библиотеки тоже может' быть обоснованным выбором.
Та небольшая доля истины, которая заключена в этом выводе, сегодня стано-
вится все меньше и меньше, благодаря таким вещам как OLE и забота о перено-
симости на другие платформы. Тем не менее, работа без какой-либо каркасной
библиотеки все еще остается возможным сценарием разработки и, в зависимо-
сти от конкретных условий, может оказаться наиболее правильным выбором.
Например, если вы собираетесь написать простую утилиту, которая должна
уместиться в тоненькую щелку, оставшуюся на одном из дистрибутивных дис-
ков, то написание ее без применения каркасной библиотеки может оказаться
решением, в каких-то случаях - даже единственно возможным (если вопрос
ставится так - либо использовать библиотеку и тем самым добавить еще один
диск к дистрибутиву, который будет затем разослан десяткам или сотням тысяч
людей, либо не использовать библиотеку, но сохранить работу - выбор
совершенно очевиден).

Если вы собираетесь использовать каркасную библиотеку, вы должны оце-
нить альтернативы очень тщательно и убедиться, что они действительно
обеспечивают те возможности, которые вам нужны, и что они делают это удов-
летворяющим вас (и ваших пользователей) способом. Это особенно актуально
в тех случаях, когда важным требованием к проекту является переносимость
между различными платформами. Поддерживается ли создание свернутых (ми-
нимизированных) программ? Как насчет поддержки перетаскивания-бросания,
DDE и OLE? Как насчет новых элементов управления Windows 95? Если вы
считаете, что вам понадобятся автоматически рисующиеся окна-списки, позво-
лят ли вам сделать это, или вам придется добиваться логически эквивалентного
интерфейса лишь теми средствами, что имеются в каркасной библиотеке? На-
сколько широко охватывается Windows API в этой библиотеке? Более широкий
охват, сам по себе, не является безусловным достоинством; например, если вы
не собираетесь много работать с «рисовальной» графикой, то мощная
поддержка GDI не имеет значения для вас и не принесет вам прибыли, какой
бы крутой она ни была по описаниям в полноцветных рекламных буклетах.

Добавления и аксессуары

Подобно тому, как ни один человек не является совершенством, ни один
компилятор не является полным и законченным средством разработки (не-
смотря на то, что многие производители хотели бы убедить вас в обратном). Вы
можете и должны наращивать ваши основные инструменты при помощи более
мелких добавлений и аксессуаров. И я хотел бы немного подробнее поговорить
о некоторых из наиболее общих дополнительных средств.

Третьесторонние библиотеки и компоненты

Ими напичканы практически все каталоги программного инструментария.
Вряд ли вам удастся открыть хотя бы одну страничку такого каталога и не быть
атакованным рекламой или описанием какого-нибудь VBX-компонента. Вот-
вот в наступление бросятся компоненты для Delphi и OCX-компоненты для
Visual Basic 4.0.

Нужно ли вам их использовать? Как всегда, ответ гласит: когда как. Это
может зависеть от вашей задачи, от ваших навыков, от имеющихся в вашем
распоряжении библиотек, от вашего кошелька и, наконец, от плана-графика ва-
шего проекта.

Идти по этой дороге следует предельно осмотрительно. Я слышал от
профессионалов многочисленные жалобы об ужасном качестве лицензирован-
ных компонентов и об отсутствии поддержки. Если вы пишете публичную
программу, тщательно исследуйте и продукты, и их производителей до того,
как привяжетесь к кому-то из них. Удостоверьтесь, что полные исходные тек-
сты либо поставляются вместе с продуктом, либо будут охотно предоставлены
вам производителем в случае необходимости. Учитывая стремительное размно-
жение разнообразнейших компонентов на сегодняшнем рынке, следует ожидать
самых неожиданных пертурбаций среди многочисленных поставщиков и
увеличения числа «продуктов-сирот». А значит, все выше и выше становится
риск заработать серьезные неприятности на свою голову, неосмотрительно
выбрав третьесторонний продукт и наткнувшись в нем на серьезную ошибку
или фатальное ограничение.

Третьесторонние отладчики

Все основные инструменты для Windows-разработки поставляются со
своими собственными отладчиками. Зачем же тогда покупать какой-нибудь
третьесторонний отладчик, если эти деньги можно пустить на существенное по-
полнение своих запасов Jolt Cola и Doritos [популярные среди программистов
питье (кола с повышенной концентрацией кофеина) и легкая закуска (что-то наподобие
чебуреков)]? Да просто потому, что существует
такая замечательная вещь, как Bounds Checker от Nu-Mega Technologies.
Bounds Checker использует отладочную информацию, помещенную в ваше
приложение, и поднимает тревогу по поводу самых разнообразных проблем:
передача недопустимых или неправильных параметров при вызове API, утечка
ресурсов, некорректное размещение и освобождение памяти и пр. Самая
замечательная черта этого инструмента - способность указать точную строку
исходного кода, в которой была обнаружена проблема.

Bounds Checker представляет собой превосходный пример высокопроизводительного
инструмента, который достоин включения в арсенал каждого Windows-
разработчика.

Системы управления версиями

Вы - не стадное животное, вы - одинокий волк, независимый и свобод-
ный, идущий своей дорогой через кибер-равнины так, как считаете нужным. А
потому зачем вам использовать систему управления версиями? Разве это не бес-
полезный в пути хлам?

Нет. Даже если вы выполняете большинство ваших работ в одиночку, вы
сможете извлечь немалую пользу от применения системы управления
версиями. Как часто вы замечаете, что вам необходимо поэкспериментировать
с какой-нибудь новой деталью программы или сделать специальную версию для
одного или нескольких из ваших пользователей? Такие случаи возникают
часто, и система управления версиями может значительно облегчить вам жизнь.
Конечно, вы можете вручную создать новое дерево каталогов и скопировать
туда все файлы проекта. Но тогда как вы будете собирать и соединять измене-
ния из разных вариаций одного проекта в новую версию? И как вы будете осу-
ществлять частичный отказ от изменения, затронувшего дюжину исходных
файлов?

Разумеется, чем больше ваша команда, тем критичнее становятся подобные
проблемы. Если, работая в командном проекте, вы хоть раз сталкивались с тем,
как чьи-то важные изменения пропадали или, еще хуже, лишь частично перено-
сились в очередную версию, то вы уже знаете, в какую мороку и угрозу для
качества это может вылиться.

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

Инсталляционные программы

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

Как показывает практика, небольшая простая инсталляционная программа
(а не какой-то из стокилобайтных бегемотов, которые рекламируются во всех
программистских журналах) может сохранить вам (да, именно вам) массу
времени в перспективе. Далее если для установки вашей программы требуется
всего лишь копирование нескольких файлов в соответствующие каталоги, ваши
усилия окупятся сторицей. Пользователи вашей программы получат простой,
автоматизированный способ установки, а вам не нужно будет беспокоиться о
порой весьма творческом подходе людей к размещению и редактированию фай-
лов, перезаписыванию системных динамических библиотек и т. д. и т. п.

Чем более сложен процесс инсталляции вашей программы, тем важнее
вопрос об автоматизации этого процесса. С приходом Windows 95 и ее на-
вязчивой идеи о реестре, инсталляция вообще стала критическим вопросом. А
если вы будете хотя бы какое-то время поддерживать и Windows 3.1, и Win-
dows 95 (что, я уверен, придется делать многим из нас), то какой-нибудь срав-
нительно небольшой инсталляционный пакет типа Install Pro от Eshalon Devel-
opment наверняка окупится легко и быстро.

Оптимизация: бесплатный сыр подан!

Создайте новый проект при помощи Visual С++ или любого
другого аналогичного пакета и взгляните на настройки проекта.
Вы увидите кое-что интересное: настройки для конфигурации «Re-
lease» не только отключают генерацию отладочной информации
(что естественно), но но умолчанию включают довольно агрессив-
ную оптимизацию (как правило, «Оптимизацию скорости» или
«Оптимизацию размера»).

Как же. черт возьми, это случилось? Когда это мы сообща со-
гласились с тем, что финальные версии продуктов, поставляемые
конечным пользователям, должны иметь высокооптимизирован-
ный код? И когда это мы решили, что текущее (да и вообще какое-
нибудь, если на то пошло) поколение компиляторов C/C++ доста-
точно надежно справляется с такой деликатной задачей, как опти-
мизация? Насколько я знаю, ответ на оба вопроса одинаков: нико-
гда. Потому что нас никто об этом не спрашивал.

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

Общепризнанная мудрость гласит, что желание использовать
отладочные настройки на всем протяжении процессов кодирова-
ния и тестировании диктуется следующими причинами:

1. Такие полезные средства отладки, как TRACE() и ASSERT()
полагаются на правильное значение препроцессорного симво-
ла отладки (DEBUG).

2. В целевом исполняемом файле должна присутствовать отла-
дочная информация.

3. Компиляции должны проходить настолько быстро, насколько
возможно, поскольку масса времени программиста будет
тратиться на циклы редактирование/компиляция/тест. Если
при этом исполняемый файл не будет максимально быстрым
или минимального размера, - не беда, поскольку настоящие
пользователи даже не увидят этот файл.

Как только версия будет готова, вы захотите нейтрализовать
TRACE() и ASSERT(), удалить отладочную информацию из ис-
полняемого файла и выжать максимально возможную производи-
тельность из законченной программы.

Все это совершенно разумно и находится в полном согласии с
теми настройками проекта, которые по умолчанию заданы в наших
C/C++ оболочках. Самой большой проблемой этого розового сце-
нария является тот факт, что оптимизаторы несовершенны. Они
иногда вводят ошибки в корректную программу, совершая свои
колдовские манипуляции с кодом. (Если вы не верите, что опти-
мизация действительно граничит с черной магией, загляните в
мартовский номер Microsoft Systems Journal за 1995 год и
почитайте про возможности Visual С++ 2.0 в этой области: опти-
мизаторы перемещают код с места на место, удаляют его, перепи-
сывают его. Если вы примете во внимание общую картину качест-
ва больших Windows-приложений, и в том числе компиляторов, и
даже после этого не обеспокоитесь по поводу творческого подхода
оптимизаторов к вашему коду, я вам уважительно намекну: вы
просто не обращали внимания, сэр.)

Загляните на. Microsoft Development Network CD, и вы найде-
те несколько подтвержденных ошибок и проблем, для которых
рекомендуемым решением является отключение оптимизации (ли-
бо полностью, либо частично). А еще почитайте врезку «С++: не-
доварен, впрочем, как и его компиляторы» на стр. 188, и обратите
ваше внимание на то, как за тот короткий промежуток времени, в
течение которого Windows/DOS Developer's Journal вел свою ко-
лонку «Bug++ of the Month» («Ошибка месяца»), были отмечены
две ошибки оптимизаторов - одна у Borland С++, а другая у Mi-
crosoft Visual С++.

Когда вы разрабатываете и тестируете продукт с одними на-
стройками оптимизатора, а потом отгружаете его пользователю с
другими, вы делаете ничто иное, как играете в русскую (хоть и
компьютерную) рулетку. Когда-нибудь ваша программа щелкнет
по каморе, которая окажется непустой.

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

Многие полагают, что оптимизацию не следует включать
слишком рано (в цикле разработки), поскольку она существенно
замедляет компиляцию и компоновку. Для реальных проектов это
предположение является довольно обманчивым. Взгляните,
например, на результаты моего небольшого эксперимента с раз-
личными установками оптимизации при сборке исходного кода
программы WordPad при помощи Visual С++ 2.2:

Установки оптимизации

Время полной сборки

Время
перекомпиляции
одного модуля
и компоновки


Отключена

5:07

35 секунд

Минимальный размер

5:42

36 секунд

Максимальная скорость

5:48

38 секунд


В случае полной сборки я считаю эти различия несуществен-
ными. Как только она начинает занимать больше двух минут, из-
менение ее длительности всего на 13% (разница между 5:07 и 5:48)
уже не имеет значения; запуская такую сборку по нескольку раз
на дню, вы вскоре обнаружите, что слишком много времени
тратится уже не на кодирование, а на раскладывание пасьянсов,
саперное дело или упражнения на гитаре. Что же касается час-
тичных сборок, то там настолько превалируют временные затраты
на компоновку, что наличие или отсутствие оптимизации прак-
тически невозможно заметить.

Как всегда, в заключение необходимо ответить на законный
вопрос: и что же со всем этим делан, в реальном мире? Для пуб-
личных программ я могу дат), следующие рекомендации:

1.  Будьте предельно скептичны по отношению к любой оптими-
зации компилятора. Вы должны обязательно исследовать «по-
служной список» компилятора в этой области: задавать
вопросы в соответствующих телеконференциях, тщательно
просматривать публикуемую поставщиком информацию об
ошибках (если таковая имеется) и т. д.

2.  Не соглашайтесь на предлагаемые производителем настройки
без предварительной оценки того, насколько хорошо они вам
подходят в текущем проекте. Как я уже не раз говорил, нельзя
прекращать исследование выбранного инструмента. Не подда-
вайтесь ни одной из отдельных деталей продукта, не проверив
ее, особенно если ошибка в ней может грозить серьезнейшими
неприятностями вам и вашим пользователям.

3.  Как можно раньше определитесь с тем, какие опции оптими-
зации вы хотите использовать для финальной версии, а затем
все тестирование осуществляйте именно с этими опциями. Во-
обще говоря, вы должны использовать оптимизацию только
тогда, когда вы уверены, что выигрыш от этого будет реаль-
ным и ощутимым, и что он перевесит риск. Мой личный опыт
знакомства с компиляторами говорит за то, что в большинстве
реальных проектов использование оптимизации не имеет ни-
какого смысла. Так ли это в вашей конкретной ситуации -
решать вам. Главное - помните одно простую вещь: со-
бираясь потанцевать с дьяволом, удостоверьтесь, что знаете
все па.

Что будет с OWL?

Недавнее (относительно времени написания этих строк)
прошлое компании Borland может служить интересной и
поучительной иллюстрацией к разговору о выборе инструментария
и, косвенно, поставщика. Конец 1994 года и начало 1995 года
были для Borland периодом тяжелейшего финанасового положе-
ния: повсюду распространились слухи о ее неминуемом банкрот-
стве, и в «кулуарах индустрии» обсуждались перспективы скупки
Borland той или иной компанией (общепризнанным фаворитом то-
гда считалась Novell, и такой сделки ожидали буквально со дня на
день).

Выпуск в свет Delphi в марте 1995 года стал спасительным
мостиком через пропасть, у края которой топталась Borland. Даже
несмотря на резкую (и, на мой взгляд, заслуженную) критику в
адрес документации, продажи Delphi заметно ширились, и тучи на
небосклоне Borland стали рассеиваться. К сожалению, нельзя ска-
зать, что это вызвало адекватное «просветление» в судьбе мно-
гочисленных программистов - поклонников Borland Pascal for
Windows. Да, они получили на вооружение выдающиеся возмож-
ности Delphi по повторному использованию кода, с одобрением
приняли некоторые новые расширения Object Pascal и более
современную каркасную библиотеку VCL (Visual Component Li-
brary), обещавшую плавную миграцию на 32-разрядную плат-
форму в ближайшем будущем. Но впридачу разработчики по-
лучили серьезнейшую проблему: VCL оказалась несовместимой с
OWL, и при этом не было предложено хоть какого-нибудь более-
менее легкого пути для переноса BWP/OWL-кода на Delphi/VCL.
В результате, все обладатели приличных наработок на BPW/
OWL попали в безвыходное положение.

Как выяснилось из моей переписки с двумя высокопоставлен-
ными сотрудниками Borland но поводу этой проблемы, ловушка
на самом деле была двойной: в планы Borland совсем не входил
перенос OWL на 32-разрядную платформу (хотя 32-разрядная
версия OWL для С++ уже существовала). Более того, мне сооб-
щили, что Borland усиленно занимается поисками какой-нибудь
третьесторонней компании, которая взялась бы выполнить этот
перенос (вплоть до вознаграждения за это в виде передачи этой
третьей стороне права на маркетинг результирующей версии). Та-
ким образом, весь существующий BPW/OWL-код оказался не
только в тисках старой каркасной библиотеки, но и в капкане 16
бит.

В итоге, у разработчиков осталось совсем мало вариантов
дальнейших действий:

1. Переносить код на Delphi/VCL ценой всех необходимых для
этого переделок.

2. Переносить код на Borland С++ 4.5 (текущую на тот момент
версию С++). В этом случае код мог бы по-прежнему опирать-
ся на OWL (пускай и на несколько иной ее диалект), но это
потребовало бы буквально построчной трансляции с Pascal на
С++ (что, как известно, является более чем сомнительным
удовольствием, если количество строк в программе превышает
пару-тройку сотен).

3.  «Выбросить» старый код и переписывать его с нуля, исполь-
зуя новый пакет.

4.  Ничего никуда не переносить и продолжать жить с 16-разряд-
ными версиями своих продуктов.

В такой ситуации для небольшой программы в две-три тысячи
строк особой проблемы выбора нет: можно отвести пару недель на
изучение VCL, после чего совершить перенос по пути 1, попутно
создав себе неплохой плацдарм для последующего прыжка на 32-
разрядную платформу (при появлении Delphi32, разумеется).
Возможно, именно поэтому не возникло никаких вопросов и у
многих (если не у большинства) шумных поклонников Borland -
по крайней мере, я лишь очень изредка видел обсуждение этой
темы в форуме DELPHI в CompuServe. (Может быть, перейдя на
Delphi, программисты занимались написанием только новых
программ, выбросив свой старый код?)

Но что же было делать тем, у кого накопился действительно
солидный багаж исходных кодов на BPW/OWL? Так, например,
один из моих клиентов - обладатель нескольких проектов с сум-
марным объемом исходных текстов примерно в 200,000 строк - в
один день оказался без единого экономически осмысленного пути
для продолжения этих проектов. У меня самого есть более 100,000
строк кода, написанных на BPW/OWL, конвертация которых
любым из вышеописанных способов была бы для меня неоправ-
данно дорогой. Поэтому я так и продолжаю до сих пор надеяться,
что кто-нибудь «подберет» OWL и перенесет его на 32-разрядную
платформу (увы, насколько мне известно, пока к этой задаче еще
никто не приступил).

Суть вышеизложенных фактов - вовсе не в том, чтобы лиш-
ний раз поругать Borland; наоборот, я на протяжении многих лет
был в числе верных сторонников этой компании и предлагаемого
ею программистского инструментария. Но после того, как с начала
90-х годов их техническая поддержка покатилась вниз по наклон-
ной, их отказ от поддержки огромного количества BPW/OWL-
кода своих заказчиков окончательно заставил меня решительно из-
менить свое отношение к Borland - оно стало точно таким же
скептическим, как и к другим компаниям. И тот печальный факт,
что я столь долго давал им фору (в смысле доверия) относительно
других производителей, является лишь доказательством моей
ошибки - чрезмерной доверчивости к поставщику (ошибки, ко-
торую вам не следует допускать). Справедливости ради я хотел бы
отметить, что послужной список Borland по части отказов от своих
продуктов и стандартов определенно не самый худший по сравне-
нию с другими компаниями. Borland - это всего лишь отдельный
пример.

Произойдет ли чудесное возрождение Borland? Лично я на-
деюсь на это, поскольку всем нам стало бы только лучше от ожив-
ления конкуренции и расширения выбора на рынке компиляторов.
Более того, я ожидаю довольно заметного улучшения положения
Borland уже в ближайшем будущем.

С++: недоварен, впрочем, как и его компиляторы

Я пишу эти строки в августе 1995 года: стандарт ANSI для
языка С++ все еще не появился на свет, а компиляторы вовсю
соревнуются друг с другом по количеству реализованных новых
элементов грядущего стандарта. Например, Visual С++ 1.5 не под-
держивает и никогда не будет поддерживать шаблоны. Ни один из
известных мне компиляторов не поддерживает новые операции
приведения типа, области определения имен и булевский тип (да,
мы столько лет на все лады определяли свои собственные вариан-
ты типа BOOL, и теперь, наконец, С++ вот-вот получит на
вооружение настоящий булевский тип). Как же долго писали мы
публичные программы, фактически используя бета-версию языка
и его прото-компиляторы!

И далее несмотря на столь долгое и широкое практическое
применение С++, сегодняшний урожай его компиляторов для
разработки Windows-приложений отличается удивительной
незрелостью. Пролистав подшивку старых номеров Windows/
DOS Developer's Journal и в очередной раз заглянув в превосход-
ную рубрику Марка Нельсона «Bug++ of the month» («Ошибка
месяца»), я подумал, что небольшая выборка из нее могла бы по-
служить отличной объективной иллюстрацией качества современ-
ных компиляторов С++:

•         Октябрь 1994 года: Watcom С++ 10.0 не выполняет вызов де-
структора для автоматического объекта, когда возврат из
функции производится из тела оператора switch.

•         Ноябрь 1994 года: Borland С++ 4.02 генерирует вызовы де-
структоров для несуществующих автоматических объектов.

•         Декабрь 1994 года: Visual С++ 1.5 генерирует сотни ложных
вызовов конструктора, если включена оптимизация (опция /
Ох).

•         Январь /995 года: Visual С++ 1.5 не выдает предупреждение
о попытке возвращения функцией ссылки на локальную
переменную (далее на самом высоком уровне предупрежде-
ний).

•         Февраль 1995 года: Borland С++ 4.5 не выполняет очистку
стека при включенной оптимизации инструкций процессор,
i386.

•         Март 1995 года: Visual С++ 2.0 использует неправильное
значение по умолчанию в конструкторе.

•         Апрель 1995 года: Borland С++ 4.5 некорректно обрабатывает:
16- и 32-разрядные вызовы iostream, приводящие к испорчен-
ным выходным данным.

•         Май 1995 года: Visual С++ 2.0 некорректно обращается с
функциями-друзьями и шаблонами.

•         Июнь 1995 года: Borland С++ 4.5 не позволяет отключить
RTTI (динамическую информацию о тинах), если пред-
варительно не отключена обработка исключительных ситуа-
ций.

•         Июль 1995 года: Visual С++ 1.5 генерирует некорректный код
для процессора i386 (опция /GЗ).

•         Август 1995 года: Borland С++ 4.5 конвертирует еnum в не-
связанный с ним класс с целью проверки его значения.

Разумеется, если вас интересуют какие-то из этих ошибок -
их влияние на ваши конкретные разработки, пожалуйста, не пола-
гайтесь на мои сжатые описания, приведенные выше. Лучше отко-
пайте соответствующие номера Windows/DOS Developer's Jour-
nal или скачайте соответствующие файлы из CompuServe (SDFO-
RUM, library 7), а затем самостоятельно ознакомьтесь с
оригинальными подробными описаниями этих ошибок.

Большинство (если не все) из перечисленных мною ошибок
должно быть устранено к моменту выхода этой книги в свет, но я
и не ставил себе целью снабдить вас более-менее полным списком
ошибок компиляторов С++. Просто этот список, совместно с
отчетами о проблемах, публикуемыми на Microsoft Developer Net-
work CD, и многочисленными третьесторониими «подборками»
ошибок и сбоев - все это неопровержимые свидетельства незрело-
сти существующих компиляторов, практически всех компиляторов
без исключения. И если принять во внимание стремительность
всех изменений, происходящих ныне в индустрии, отчаянную
борьбу производителей компиляторов за 100-процентное соответ-
ствие своих творений стандарту ANSI, а также все те бесконечные,
следующие одна за другой модернизации, необходимые для под-
держки все новых и новых возможностей Windows - трудно ожи-
дать   существенного   улучшения   качества   компиляторов   в
обозримом будущем. (В нашем бизнесе «обозримое будущее» -
довольно растяжимое понятие: от пяти минут до года. Но если го-
ворить именно о компиляторах, то я не думаю, что у них есть
реальные шансы перевести дух и дозреть по крайней мере в
течение ближайшего года. А потом - с появлением Windows 96 и
Cairo - вся карусель неизбежно закрутится снова.

Как же нам жить и работать в таких условиях? Конечно же,
нельзя прятать голову в песок и убеждать себя, что эти проблемы
не существуют на самом деле - это было бы безответственно и ни-
коим образом не могло бы повысить качество наших публичных
программ. Наша единственная опора - постоянно помнить о том,
что современные компиляторы С++ бурно и безостановочно
эволюционируют и снаружи, и изнутри, и что это неизбежно будет
приводить к наличию в них тех или иных ошибок. Это означает,
что иногда ошибки наших программ в действительности являются
не нашими собственными ошибками, а подножками со стороны
компиляторов, и что мы просто обязаны быть в любую минуту го-
товы к подобным случаям. (Ошибки компиляторов во многом
сродни таким проблемам, как поломка жесткого диска: вопрос не
в том, случится это или нет, вопрос в том, когда это случится.)

Разумеется, все мы должны делать и еще одну вещь -
стараться сообщать обо всех найденных нами ошибках в компиля-
торах (да и в других программных продуктах тоже) их авторам и
разработчикам. В конце концов, всегда есть шанс на то, что произ-
водитель устранит именно вашу проблему в ближайшей новой
версии или «заплате», или хотя бы предложит приемлемый способ
обхода.

Delphi32: Pascal достигает совершеннолетия

Компиляторы языка Pascal от Borland занимали заметное ме-
сто на рынке еще со времен первой администрации Рейгана. Прак-
тически сразу за ними прочно закрепилась репутация быстродей-
ствующих, практичных, но в каком-то смысле «игрушечных» ин-
струментов разработки, не слишком пригодных для реальной
работы. И хотя у этих продуктов определенно были какие-то свои
ограничения и причуды, я думаю, что ярлык «игрушечности» был
в значительной степени необоснованным.

Последнее достижение Borland, Delphi, довольно быстро
устраняет эту проблему с имиджем, а грядущая версия Delphi32
должна окончательно стереть все оставшиеся следы. Поэтому по-
звольте мне обратить ваше внимание на наиболее интересные воз-
можности Delphi32, имеющие отношение к теме этой главы и всей
книги в целом:

•         Полностью 32-разрядный компилятор, без 64-килобайтных
ограничений. Я делаю минутную паузу, чтобы вы смогли объ-
явить и отпраздновать День Освобождения Паскалистов.

•         Полная совместимость с Delphi16, включая поддержку VCL.

•         Полная поддержка новых элементов управления Windows 95.

•         Значительно улучшенный компоновщик. Я не могу и не дол-
жен сообщать результаты аттестационных тестов бета-версии
какого-либо программного продукта, но мой не очень об-
ширный опыт тестирования Delphi32 уже позволяет уверенно
утверждать, что эта версия компилирует и компонует очень
быстро, создавая исполняемые файлы еще меньшего размера,
чем это делала ее 16-разрядная предшественница.

•         Способность создавать 32-разрядные ОВJ-файлы, которые
можно связывать с проектами, разработанными при помощи
C/C++. Возможно, пуристы от Pascal презрительно усмехнут-
ся в адрес этой детали, но лично я считаю, что это один из
важнейших залогов перспективного успеха Delphi32. До сих
пор не существовало ни одного практичного способа «смеши-
вать» Pascal и C/C++ от Borland в одном проекте. (Да, прими-
тивная возможность прицеплять OBJ-файлы к Pascal-проекту
уже была, и тем более всегда был вариант обеспечения мирно-
го сосуществования языков через механизм DLL. Но эти спо-
собы не очень пригодны для. большинства реальных проек-
тов.) Теперь же можно будет написать модуль на Pascal, ском-
пилировать его в OBJ-файл и затем использовать в другом
проекте точно так же, как если бы этот модуль был изначаль-
но написан на C/C++. А это даст возможность большим
программистским коллективам и компаниям тестировать Del-
phi32 на своих реальных проектах выборочно, без необходи-
мости прибегать к решениям типа «пли все, или ничего». Как
правило, корпоративные разработчики имеют такие большие
объемы невыполненных заказов, что никак не могут позво-
лить себе роскошь изолированного тестирования нового ин-
струментария; они чаще всего вынуждены производить тес-
тирование прямо в боевых условиях. Поддержка стандартных
OBJ-файлов в Delphi32 как раз и позволит им осуществлять
такое тестирование. И я готов предсказать, что разработчики
будут широко тестировать Delphi32, а впоследствии выберут
этот компилятор в качестве основного инструмента для боль-
шого числа своих проектов.

Как серьезный недостаток, изменение размера типа integer
с 16 на 32.
К сожалению, в этом вопросе Delphi32 последовал
дурному примеру C/C++ и даже ввел новый 16-разрядный це-
лочисленный тип smallint (32-разрядный целочисленный
тип - longint уже существовал в Delphi и раньше). Все это
мне кажется довольно глупым, поскольку без какой-либо
реальной необходимости делает часть существующего кода не-
корректной и меняет размер многих определенных пользова-
телями записей.

Вообще говоря, предсказания в области программного
обеспечения - скорее дело, подходящее лишь для лунатиков и
эгоцентристов. Тем не менее, я готов встать на край карниза и
предсказать, что быстрый успех Delphi 16 станет хорошим трам-
плином для Delphi32, a Delphi32, в свою очередь, повернут ход
событий в другую сторону (в сторону, противоположную C/C++),
хотя это произойдет, вероятно, не раньше конца 1996 года. Я убе-
жден, что сегодня существует огромнейший неудовлетворенный
спрос на достойную альтернативу для C/C++, и что в области
программирования для Win32 она уже отчетливо видна.