Б. Страуструп - Дизайн и Эволюция C++. 2006 (1160775), страница 50
Текст из файла (страница 50)
раздел 13.2.2). Идентификация типа во время исполнения позволяет восстановить информацию о типе, утраченную из-за того, что переданный библиотеке объект возвращен обратно в виде экземпляра менее конкретного типа, то есть экземпляра базового класса (см. раздел 14.2.1). Конечно, зто лишь одно из возможных применений указанных языковых средств, но рассмотрение их в качестве способа поддержки построения программ из независимо разработанных библиотек может быть полезным. Рассмотрим в этом ключе множественное наследование (см. раздел 12.1). Библиотеки в духе Вша!!га1к основаны на одном «универсальномь корневом классе.
БИИИИПФс Библиотеки Сложности появляются, когда таких класса два. Но если библиотека написана для конкретной предметной области, то иногда помогает множественное наследование: с1авв орз гров риЫ ьс пгардгсвов)есв, р..ыьс равававеоЬ)есс О; Пераз реп имая проблема возни кает, когда оба «универсальных» базовых класса предоставляют один и тот жс сервис, например идентификацию типов во время исполнения и механизм ввода/вывода объектов. Некоторые из подобных проблем лучше всего решаются путем выноса общих возможностей в стандартнук> библиотеку нлн реализации их в самом языке. Можно также поместить общую функциональность в иерархию классов с новым общим корнем.
Однако объединить подобным образом «универсальные» библиотеки так просто не получится, и это необходимо учитывать при проектировании библиотеки. Управление памятью — еще одна область, в которой имеется целый ряд проблем как для разработчиков библиотек, так и для пользователей, вынужденных обращаться сразу к нескольким библиотекам (см. раздел 10.7). 8.3. Ранние библиотеки Первым кодом, написанным на С»тпЬ С!аваев, была библиотека для поддержки многозадачности (см. раздел 8.3.2.1), в которой реализовались 31ац!а-подобные механизмы параллельности для моделирования 13ггопвггцр, 1980Ь1.
Первыми реальными программами, в которых использовалась эта библиотека, стали приложения для моделирования сетевого трафика, проектирования топологии печатных плат и т.д. Эта библиотека интенсивно используется и по сей день. Стандартная библиотека С вЂ” без каких бы то ни было дополнительных затрат — поставлялась вместе с С++ с самого начала, равно как и все прочие написанные на С библиотеки. Классические типы данных — строки символов, массивы с контролем выхода за границы, динамические массивы и списки — использовались как примеры при проектировании и тестировании ранних реализаций С++ (см.
раздел 2.14). Сначала работы, связанные с такими контейнерными классами, как списки и массины, сильно осложнялись из-за отсутствия в языке поддержки параметризованных тинов (см. раздел 9.2.3). Приходилось обходиться макросами. Макросы, поддерживаемые препроцессором С, помогли нам приобрести опыт, пригодившийся затем цри разработке параметризованных типов. Сами классы проектировали мы с Джонатаном Шопиро. В 1983 г. он реализовал классы строк и списков, которые широко применялись в Ата Т и легли в основу классон, вклк>ченных в нынешнюю библиотеку «Стандартных компонентов». Эта библиотека была разработана в Вей ЕаЬв и продается через 1)81..
Создание ранних библиотек шло параллельно с проектированием языка, в частности с проектированием механизма перегрузки. Основная цель первых библиотек для работы со строками и списками — предоставление относительно простых классов, которые можно было бы использовать как строительные блоки в приложениях и при создании более сложных библиотек. Ранние библиотеки '16ИИИИИН Альтернативная возможность — написание кода непосредственно на С или С+~-, поэтому эффективности уделялось особое внимание. Предпочтение было отлано замкнугым классам, а не иерархиям и встраиванию критических по времени операций.
Кроме того, классы проектировались так, чтобы их можно было применять в традиционно написанных программах без крупных переделок или дополнительного обучения программистов. В частностц, не лелалось никаких попыток предоставить пользователю возможность модифицировать поведение этих классов с помощью замещения виртуальных функций в производных классах.
Если бы пользователю понадобился общий модифицируемый класс, его можно было бы написать, взяв «стандартный» в качестве основы. Например: с1авв Зятьпр 1 // простой и эффективней // ): с1авв му всг)пс [ // обдий и адаптируемый ясггпр гер; // риЫ1с: // чьтяпа1 чо)б аррепб)совая Зят1пра); ч)гспа1 чо1д аррепд)сопев му всг)пда); // ): 8.3Л. Библиотека потокового ввода/вывода Семейство функций рг1пс1 нз библиотеки С вЂ” эффективный и во многих случаях удобный механизм ввода/вывода. Однако эти функции не обеспечивают контроля типов и не допускают применения определенных пользователем типов (классов и перечислений). Поэтому я начал думать о безопасной, лаконичной, расширяемой и эффективной альтернативе ргфпс8.
Отчасти вдохновение снизошло на меня, когда я прочел последние полторы страницы «Ада Яаг)опа!с» («Обоснование языка Ада») [1сЬ[лаЬ, 1979[, где доказывается, что нельзя построить лаконичную и безопасную библиотеку ввода/вывода без специальной подлержки со стороны языка. Я воспринял этот категоричный тезис как вызов. В результате появилась библиотека потокового ввода/вывода, впервые реализованная в 1984 г. и описанная в работе [81гоцяггцр, 1985[. Вскоре после этого Дейв Пресотто (Паче Ргеяоио) переписал ее с целью улучшить эффективность за счет отказа от использования стандартных функций ввода/вывода из библиотеки С. Вместо этого он напрямую обращался к средствам операционной системы.
Работа была сделана без изменения интерфейсов потока. Для знакомства с новой кош)епцией потокового ввода/вывода рассматривался такой пример: грт1псб)всбегг, "к = «в)п",к); Поскольку 1рг1пб й 1) нс проверяет переданных аргументов, основываясь на предположении, что они соответствуют форматной строке, зто небезопасно. Обратимся к работе [51гоцвггцр, 19851, где в связи с атим говорится: БПИИИИИВ Библиотеки «Бсли бы х принадлежало определенному пользователем типу, например солтр1ех, то нельзя было бы задать формат вывода х ток же спокойно, как для типов, о которых рг1пс1 «знаеть (к примеру, «я и «г)). Для вывода комплексного число программисту пришлось бы писать последовательность вызовов функций, например; трг1псг(ясс)егг,"х = рцг солгр1ех(ягаегг,х)г 1рг1пСГ(ягг)егг,'(и")г Это неизящно.
А уж в С++-программе, где применяется много определенных пользователем типов, такая запись была бы совсем неудобной. Безопасного использования типов и единообразия можно достичь с помощью одного перегруженного имени для семейства функций вывода. Например: рцс(ясс)егг,"х = ")т рцг(ясс)егг,х)г рпс(ясаегг,"1п")г Тип аргумента определяет, какая именно функция рцс будет вызвано. Однако и зто слишком многословно. В С++ для решения задачи испол~куется поток вывода, дпя которого оператор «имеет смысл «вывести вж сегг « "х = " «х « "1п" г где сегг — стандартный поток вывода для ошибок (эквивалентный ясаегг из С).
Бели х имев~ тип 1пс и равно 123, то результат зтаго вывода будет таким: х= 123 и в конце — символ новой строки. Донный способ можно использовать, если только х принадлежит типу, для которого определен оператор «, а определить такай оператор в новом типе просто. Так, если х имеет определенный пользователем тип сот«р1ех и ровно (1, 2. 4), то это предложение выведет в сегг строку х = (1,2.4) Такой потоковый ввод/вывод реализован с помощью средств языка, доступных любому программисту. Как в С, так и в С++ нет встроенных в язык средств ввода/вывода. Потоковый ввод/вывод включен в библиотекуь. сегг « "х = " « х; где х имеет тип 1пс, интерпретируется как (сегг.орегасог<«("х = ")),орегясог«(х) Возможность использования вместо именованной функции оператор вывода первоначально предложил Дуг Макилрой по аналогии с операторами перенаправления ввода/вывода в командных интерпретаторах (Л«ПХ (>, », ( и т.д.).
Требуется только, чтобы оператор возвращал свой левый операнд для передачи последующим операторам: «Функция арегас о г «возвращает ссылку на объект о я с геялг, для которого она вызывалась, с тем чтобы можно было применить к ней следующий объект оясгеап~ Например, предложение Ранние библиотеки 16ИИИИИИИ В частности отсюда следует, что при печати в одном предложении нескольких элементов ани будут выведены в ожидаемом порядке: слева направо>. Если бы я решил использовать обычную именованную функцию, то пользователю пришлось бы писать кол, как в последнем примере. Для реализации ввода и вывода рассматривалось несколько операторов: «Оператор присваивания подошел бы и для ввода, и для вывода, но нас не устраивает свойственная ему ассоциативность. То есть соцс = а=Ь интерпретировалось бы кок соцс= ) а=Ь) .