Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 204
Текст из файла (страница 204)
Как правило, это объясняется их широкой распространенностью и бесплатностью. Имея выбор, ни один профессионал не станет работать на таком антиквариате. Для новичков же применение устаревших компиляторов оборачивается серьезными скрытыми потерями. Например, недостаток современных языковых средств и библиотек заставляет их бороться с проблемами, которые уже были успешно решены в новых реализациях. Кроме того, наносится серьезный вред стилю написания программ и порождает неточный взгляд на то, каков же язык С++ на самом деле. Наилучшее подмножество для первичного изучения и использования вовсе не сводится к набору низкоуровневых средств (и не к общему подмножеству языков С и С++; 81.2). Чтобы облегчить изучение и получить правильное представление о языке С++, я рекомендую опереться на стандартную библиотеку и шаблоны.
Первая коммерческая версия С++ появилась в 1985 году. Она соответствовала описанию языка, представленному в первом издании настоящей книги — там не было множественного наследования, шаблонов, средств КТТ1, исключений и пространств имен. Сегодня я не вижу смысла пользоваться реализацией, которая не поддерживала бы хотя бы некоторой части из перечисленных свойств. В 1989 году я добавил к языку множественное наследование, шаблоны и исключения. К сожалению, ранняя поддержка шаблонов и исключений оставляла желать лучшего.
Если ваша реализация относится к этой фазе развития С++ — срочно выполните ее обновление до современного состояния. В общем случае разумно применять компиляторы и среды, более-менее соответствующие стандарту и минимально опирающиеся на неопределенные, зависящие Приложение В. Совместимость 954 от реализации средства. Выполняйте проектирование в предположении, что вам доступны все современные возможности, и только после этого разыскивайте необходимые компиляторы и инструменты. Это помогает улучшить структуру программы и облегчить ее сопровождение по сравнению со случаем, когда применяется минимальное общее подмножество языка.
Также будьте осторожны с применением зависящих от реализации расширений языка и делайте это лишь в случае крайней необходимости. В.З.1. Заголовочные файлы Традиционно все заголовочные файлы имели расширение .й. То есть реализации С++ предоставляли заголовочные файлы <тар.й>, <юзггеап!.й> и тд. А для совместимости они так делают и сейчас. Когда комитету по стандартизации понадобились заголовочные файлы для пересмотренной версии стандартной библиотеки и для новых библиотечных средств, их именование превратилось в небольшую проблему — использование старых й-имен вступило бы в конфликт с задачей поддержания совместимости. Кроме того, применение суффиксов й все равно избыточно, так как <> и без того указывают на стандартные заголовочные файлы.
В итоге, стандартная библиотека предоставляет заголовочные файлы с бессуффиксными именами, такими как <тар> и <!оа!геат>. Объявления в этих файлах помещены в пространство имен зМ, в то время как старые заголовочные файлы (с расширениями й) размещают объявления в глобальном пространстве имен. Рассмотрим пример: ()1пс(иае <1оатсеаю> 1пт та(п ( ) ( эта::сои«< "НсНо, пот!а! ',и"; Если у вас не получается скомпилировать этот пример, попробуйте более традиционную версию: ()юс!иае <1оа!сеат .
й> 1п! юа1п ( ) ( сои! « "Нейо, пог1а! тп" г ) Некоторые из наиболее серьезных проблем с переносимостью возникают из-за непереносимых заголовочных файлов, причем стандартные заголовочные файлы вносят здесь лишь малую долю. Часто программы зависят от множества заголовочных файлов, присутствующих не на каждой системе, от объявлений, которые на разных системах располагаются в разных заголовочных файлах, и от объявлений, кажущихся стандартными (они располагаются в заголовочных файлах со стандартными именами), но не входящими ни в какие стандарты.
Полностью удовлетворительных решений проблемы несовместимости заголовочных файлов не существует. Самая общая идея заключается в максимальной локализации (компактизации) различий и введения дополнительного уров- В.З. Старые реализации Сч-+ ня косвенности, дабы избежать прямых зависимостей от несовместимых заголовочных файлов. К примеру, пусть необходимые нам объявления на разных системах располагаются в разных заголовочных файлах. Тогда можно включить в приложение единственный промежуточный (специфичный для данного приложения) заголовочный файл, который, в свою очередь, будет включать разные заголовочные файлы для разных систем. Аналогично, если некоторая функциональность немного по-разному предоставляется на разных системах, мы можем обращаться к ней через специфичные для приложения интерфейсные классы и функции.
В.З.2. Стандартная библиотека Неудивительно, что реализации библиотек С++, предшествовавших стандарту, могли не содержать некоторых частей стандартной библиотеки. В большинстве своем они будут содержать потоки ввода/вывода, нешаблонный класс соэпр1ех, те или иные строковые классы, а также библиотеку языка С. Как правило, будут отсутствовать классы тар, йм, га1аггау и т.п.
Применяйте в таком случае сторонние библиотеки, допускающие переход к стандарту при обновлении вашей системы до современного состояния. Но в любом случае лучше использовать нестандартные классы строк, списков и ассоциативных массивов, чем возвращаться к низкоуровневому программированию в стиле С. Имейте также в виду, что доступны для бесплатного использования хорошие реализации БТ( -части стандартной библиотеки (главы 16, 17, 18 и !9).
Ранние реализации стандартной библиотеки также не совсем полны. Например, некоторые контейнеры могут не поддерживать аллокаторов (распределителей памяти), а другие могут требовать явного указания аллокаторов для каждого класса. Похожие проблемы могут касаться аргументов, определяющих политику алгоритма, например критерий сравнения: йзг<1пг> й; уэ'оlс, но некоторые реализации требуют аллокатора 1аг<шб ойосогог<1пг» й2; У оlс, но некоторые реализации не реализуют аллокаторы тар<згггпе, йесогй> т1; эу оЬ, но некоторые реализации требуют операцию < тар<з1нпя, йесогэ1, 1ею<зазпй» тзэ Пользуйтесь той версией, которую допускает ваша реализация.
Ранние реализации С++ предоставляли 1легзггеат и озггзггеат в <лгглггеат. Ь>, а не Ыгг1пйлггеат и олег(пязггеат из <лзггеат>, причем работали зти потоки напрямую с сйаг(1 (см. З21.10[261). Достандартные потоки не были параметризуемыми. В частности, шаблоны с префиксом Ьаз1с, появились только в стандарте, класс Ьэм(с юл назывался юл, а юмаге назывался ю егаге. В.З.З. Пространства имен Если ваша реализация не поддерживает пространств имен, отражайте логическую структуру программы соответствующей структурой исходных файлов (глава 9). Аналогично, применяйте заголовочные файлы для отражения интерфейсов, предоставляемых разным реализациям, а также для языка С. Приложение В. Совместимость 956 В отсутствие пространств имен вообще используйте агапе для компенсации отсутствия неименованного пространства имен.
Также для глобальных имен применяйте идентифицирующие их префиксы: сгат Ьв згг!пя 1/"...*/); зуреде1ои Ьз Ьоо1; с1азв 1ое вгг)пя! епит 1ое Ьоо1 (1ое Га(зе,уое 1гие); Внимательно выбирайте префикс. Существующие библиотеки С и С++ переполнены разными префиксами. В.3.4. Ошибки выделения памяти До появления в языке С++ обработки исключений операция пезн возвращала нуль, когда не удавалось вьщелить память. В стандарте С++ операция пе)н по умолчанию генерирует в таком случае исключение Ьав( а11ос.
В любом случае, перехватывая Ьав( адос или реагируя на возврат нуля, трудно для большинства систем предложить что-то более интересное, чем просто сообщение об ошибке. Когда по каким-то причинам нужно придерживаться старого способа индикации об ошибке выделения памяти, то в случае, если не задан пет Ьап~Иег, применение аллокатора повйгот заставляет возвращать нуль в ошибочных случаях: Х* р1 = пезг Х; 7 генерируется Ьад айос при отсутствии необходииой памяти Х* р2 = пет (по1Ьгов) Х) У возвращает 0 при отсутствии необходимой пазтти В.3.5. Шаблоны Стандарт С++ ввел некоторые новые элементы шаблонов и уточнил работу со старыми (ранее имевшимися) средствами.' Если ваша реализация не поддерживает частичную специализацию, используйте для шаблона иное имя (в противном случае он был бы специализацией): 1етр!а1е<сгавв т> огоев р1аг: рггга1е 11зг<гоы*> ( ~У... ): Если ваша реализация не поддерживает шаблонных членов шаблонов, некоторые приемы программирования станут недостижимыми.
В частности, шаблонные члены шаблонов позволяют программистам выполнять конструирование и преобразование типа с гибкостью, недостижимой иными способами (В! 3.6.2). Рассмотрим пример: гетр1а1е<с!аев Т> с!вяз Х 1 У... гетр1аге<с1авв А > Х(сопи А в а); В отсутствие шаблонных членов шаблонов этот пример придется переделать так, чтобы ограничить себя набором определенных типов: В.3. Старые реализации С++ <етр!а!в<слезя Т> с!авв Х ( // ... Х(сопи А1Ь а); Х(сопл! А2ь а); // ...
)' гетр!а!е<с!азз Т> с!азз Саша!пег ( /... риЫс: ио!г!логе() (/* используем < */) )! с1азз С!оЬ (/* для С!оЬ нет операции < */); Соп!а!пег<С!оЬ> сд! //некоторые старые реализации пытаются определить // Санга!пег< С!оЬ> нзог!О/ используйте гетр!а!е<с1азз Т> с!азз Соп!а!пег // ... риЫ!с: ) иоЫзогг() ! )з !етр!а!е<с!азз Т> гоЫ Соп!атег<Т>:: зог! () ( /* используем < */ ) с1азз С!оЬ (/" для С1оЬ нет операции < */)/ Соп!а!пег<С!оЬ> ся; //нет проблем до тех пор, пока не вызывается ся.хогг!) Кроме того, ранние реализации не допускали использования членов классовых шаблонов, объявленных позже точки использования, как в следуюшем примере: !етр!а!е<с!азв Т> с!авз 'гас!ос ( риЬДс: Та орега<ог[) (з!ее г!) (ге!игп и[!); ) //...
// и объявляется ниже рг(иа!е: Т* ю мхе гзл; )' // не найдено/ Тогда нужно либо переупорядочить объявления членов шаблона, либо расположить определения функций-членов после объявления шаблона класса. В большинстве ранних реализаций для всех функций-членов шаблонов генерировался код в момент конкретизации шаблона [(ешр[а(е шмап([акоп).
Это могло приводить к ошибкам для неиспользуемых функций-членов [5С.13,9.1). Решение состоит в том, чтобы поместить определение функций-членов позже объявления класса. Например, вместо 998 Приложение В. Совместимость Некоторые из ранних реализаций не понимают умолчательных значений для параметров шаблона (913.4.1).
В таком случае каждому формальному параметру типа шаблона нужно в обязательном порядке сопоставить явный аргумент. Например; !етр!иге<с(авв Кеу, с1азз Т, с1авз Т.Т = 1евв<Т» с!аев тар ( У... )г тор<в!г!па, гпг> т; У Оорз: умолчательные аргументы не реализованы зиар<взппл, !пг, 1евв<вгппа» зи2; У обход проблемы: все указываем явно ВЗ.6. Инициализаторы в операторах аког Рассмотрим следующий код: гоЫ/(гес1ог<с!заг>а г, зпг т) ( /ог (1пг 1=Оз з<г.
нее () аз з<=тз ++!) сои! «г(1); 1~(1== т) уеггог: ! недостижимо после конца~ох У... ) Такой код обычно работал, поскольку в изначальном определении С++ область видимости переменной цикла совпадала с областью видимости, в которую входил сам операторное. Если вам встретится такой код, просто расположите определение переменной цикла перед оператором 2ог: гоЫ12 (вес!ее<спас>а г, !пг т) !из!= Ог Тог(1 !<г.в!ге() аа з<=т) ++1) сои! «г(!) з (Т(з'==и ) ( У ... ) ) В.4. Советы 1. Для изучения С++ применяйте самые современные и самые полные из доступных вам реализаций стандарта С++; ВВ.З.