Б. Страуструп - Дизайн и Эволюция C++. 2006 (1160775), страница 11
Текст из файла (страница 11)
В других языках интерфейса от реализации. Разумеется, я пытался растолковать зто (см. пример в (Вггопзтгпр,1986, з7.6.2]), но почему-то не был понят. Думаю, причина неудачи в том, что мне никогда не приходила в голову простая мысль: многие (если не большинство) программистов, работая с С++, думают, что раз можно поместить представление прямо в объявление класса, описывающего интерфейс, то это обязательно нужно сделать. Я не пытался создать инструменты типобезопасной компоновки для С гу(гп С1аззев, они появились лишь в версии С++ 2.0. Однако я помню разговор с Деннисом Ричи и Стивом Джонсоном о том, что безопасность с точки зрения типов при пересечении границ единиц компиляции должна была стать частью С. Просто не было возможностей гарантировать это для реальных программ, так что пришлось полагаться на инструменты типа афпг [Кегп18Ьап, 1984].
В частности, Стив Джонсон и Деннис Ричи утверждали, что в С предполагалось обеспечить эквивалентность имен, а не структур. Например, объявления НЗИИИИИВ Язык С МФ С1аззез для этого используется структурная эквивалентность. Я отдаю предпочтение эквивалентности имен, а не структур, потому что считаю такую модель наиболее безопасной. Поэтому мне было приятно узнать, что такое решение нс противоречит С и не усложняет предоставление низкоуровневых услуг. Так появилось «правило одного определения»с каждая функция (переменная, тип, константа н т.д.) должна иметь в С++ ровно одно определение. 2.5Л. Простые реализации Желание иметь простую реализацию отчасти было вызвано необходимостью (для разработки С т«11Ь С!аззез не хватало ресурсов), а отчасти обусловлено недоверием к излишне изощренным языкам и механизмам.
Ранняя формулировка одной из целей проектирования С ччгЬ С!аззез звучала так: «для реализации языка должно хватать алгоритмов не сложнее линейного поиска». Любое нарушение этого правила — например в случае перегрузки функций (см. раздел 11.2) — приводило к семантике, которая мне казалась слишком сложной. А зачастую и к сложностям при реализации. Моей целью — памятуя опыт работы с 81шп1а — было спроектировать язык достаточно простой для понимании, чтобы привлечь пользователей, и довольно простой в реализации, чтобы заинтересовать разработчиков компиляторов. С другой стороны, относительно несложная реализация должна была генерировать код, который не уступал бы С в корректности, скорости и величине.
Пользователь, незнакомый с языком на практике и находясь в не очень «дружелюбной» среде разработки, тем не менее должен суметь воспользоваться компилятором в реальных проектах. Только при выполнении обоих этих условий можно было рассчитывать, что С ««1гЬ С1аззез, а позднее С++ выживут в конкуренции с С.
Ранняя формулировка принципа звучала так: «С «чгЬ С1аазез должен быть неприхотливым есорняком» вроде С или Гогггап, поскольку мы не можем позволить себе ухаживать за такой «розой», как А18о!68 или 81шц!а. Если мы создадим компилятор и на год уедем, то по возвращении хотелось бы увидеть его работающим хотя бы на нескольких системах. Этого не произойдет, если будет необходимо постоянное сопровождение или если простой перенос на новую машину займет больше неделим Данный лозунг был частью философии — воспитывать в пользователях самостоятельность. Всегда, притом явно, ставилась задача вырастить местных экспертов по всем аспектам работы с С++, Кстати, большинство организаций вынуждено следовать противоположной стратегии — культивировать зависимость пользователей от услуг, приносящих доход центральной службе технической поддержки, консультантам или и тем, и другим одновременно.
По моему мнению, здесь заключено фундаментальное отличие С++ от многих других языков. Решение работать в довольно примитивной — и почти повсеместно доступной — среде, обеспечивающей лишь компоновку в стиле С, породило большую проблему, связанную с тем, что компилятор С++ в любой момент времени имел лишь частичную информацию о программе.
Каждое предположение относительно программы может стать неверным, если завтра часть этой программы перепишут на каком-то другом языке (С, Гогггап или ассемблере) и свяжут с остальными модулями, возможно, уже после того, как программа запущена в эксплуатацию. Модель компоновки '6ИИИИИИИ Данная проблема имеет многообразные проявления.
Компилятору очень трудно гарантировать, что; д объект, переменная и т.п. уникальны; о информация непротиворечива (в частности, типы не конфликтуют); з объект, переменная и т.п. инициализированы. Кроме того, в С есть лишь самая минимальная поддержка концепции раздельных пространств имен, так что проблемой становится загрязнение пространства имен из-за того, что части программ создаются разными людьми. Развивая С++, мы пытались ответить па все зти вызовы, не принося в жертву фундаментальную модель и технологию, которые обеспечили переносимость и эффективность; но во времена С ччгЬ С!аззез мы просто полагались на заголовочные файлы в стиле С.
С принятием в качестве инструмента компоновщика, применяемого для программ на С, связано появление следующего правила: С++ — это просто еше один язык в системе, а не вся система. Другими словами, он выступает в качестве традиционного языка программирования и принимает фундаментальные различия между языком, операционной системой и другими важными компонентами работы программиста. Это ограничивает языковые рамки, что довольно трудно сделать для таких языков, как БтаПса!Ь или Ызр, задумывавшиеся как всеобъемлющие системы или среды.
Очень важно, чтобы часть программы на С++ могла вызывать части, написанные на других языках, и сама могла быть вызвана. Кроме того, раз С++ — просто язык, то он может применять инструменты, написанные лля лругих языков. Тот факт, что язык программирования и написанный на нем код должны быть лишь шестеренкой внутри большого механизма, принципиально важен для большинства пользователей, работающих над промышленными проектами.
Тем не менее многие теоретики, педанты и пользователи из академических кругов, очевидно, не учитывали, насколько важно чмирное сосуществование» одного языка с другими и с системами. Я думаю, что в этом одна из основных причин успеха С++. С ччгЬ С!аззез почти совместим с С на уровне исходных текстов.
Однако совместимость никогда не была стопроцентной. Например, с1азз и пеы допустимы в С в качестве имен идентификаторов, но в С ч ЫЬ С1аззез и его преемниках они являются ключевыми словами. Однако на уровне компоновки совместимость сохранена. Функции на С можно вызывать из С ччгЬ С!аззез.
Функции на С ччгЬ С!аззез можно вызывать из С, а структуры одинаково размещаются в памяти, следовательно, передача как простых, так и составных объектов между функциями, написанными на разных языках, проста и эффективна. Такая совместимость по компоновке сохранена и в С++, за несколькими простыми и явно обозначенными исключениями, которъ1е программист при желании легко может обойти (см. раздел 3.5.1). С годами я и мои коллеги пришли к выводу, что гораздо важнее совместимость на уровне компоновки, чем на уровне исходных текстов. По крайней мере, это верно, когда идентичный исходный кол лает одни и те же результаты как в С, так и в С++, или не компилируется, или нс связывается па одном из этих языков.
Язык С (а(1Ф С1авзев КИИИИИИВ 2.5.2. Модель размещения объекта в памяти Базовая модель объекта имела огромное значение для дизайна С тг]сЬ С!аваев. Я всегда точно представлял себе, как располагается объект в памяти, и рассматривал воздействие различных языковых средств на объекты. Без понимания эволюции модели объекта нельзя понять эволюцию С++.
В С ч [сЬ С]аваев объект представлял собой просто С-структуру. Иными словами, объект с1авв ясас)с ( сваг я [10]; сваг* в1п; сваг* сор; сваг* вах; уоЫ пеи(); рп)>11с: уоЫ рпвй И; сьаг рор(); ); хранился в памяти так же, как структура /* сгенерированный С-код */ ясгпсс ясас)с спас я [10]; сваг* въп; сваг* сор; сваг* вах; ); то есть спаг в [10) спаг* вгп спас* сор спаг* вах Компилятор может добавлять некоторые заполнители до и после членов структуры для выравнивания, но в остальном размер объекта равен сумме размеров членов. Таким образом, расход памяти минимизируется. Накладные расходы во время исполнения также сведены к минимуму за счет прямого отображения вызова функции-члена уоЫ всас)с.рпвй(сйаг с) ( 1г (сор>вах) еггог("стек пуст"); *сор++ = с; ) уоЫ 0(с1авв ягас)с* р) ( р->ровс('с'); ) Статический контроль типов 16ИИИИИИН на вызов эквивалентной С-функции в сгенерированном коде: гога ясас)с ровс(готя,с) /* сгенерированный С-код */ вггисс вгаск* готя; спаг с; ( ((Готя->гор)>(готя->иах)) еггог("стек пуст" ); *(гдгя->гор)++ = с; ) готс) д(р) всгосс всаск* р; /* сгенерированный С-код */ ( всаск рыяо(р,'с'); ) В каждой функции-члене указатель с)тгв ссылается на объект, для которого вызвана функция-член.