Б. Страуструп - Дизайн и Эволюция C++. 2006 (1160775), страница 14
Текст из файла (страница 14)
Но, потерпев неудачу с другими попытками изменения синтаксиса, я не хотел нарываться на неприятности еще раз и внес это изменение (когда С ччгп С!аззез превратился в С++) только по настоятельному требованию Тома Каргилла Имя структуры, объединения и к)гасса в С++ — просто имя типа и ие требует специального обозначения: ЬиГгег а; // С«» «Битвы на поле совместимости с С» продолжались несколько лет (см. также раздел 3.12).
Например, следующая запись допустима в С: еггисг Я ( Гпс а; ); 1пг. Я; кого г(аггисс Я х) ( х.а=з; // Я вЂ” переменная типа Гпг Оиа также допустима и в С гч!г!) С!аззез, и в С++, но в течение многих лет мы искали формулировку, которая в целях совместимости допускала бы такой немного странный, ио безвредный код в С++.
Ведь, если бы это было разрешено, следовало запретить следующее: кого д(5 х) ( х.а = Я; // ошибка: Я вЂ” переменная типа гпг Насущная необходимость решить данную проблему следовала из того, что в некоторых заголовочных файлах для системы ()Х!Х, прежде всего в Ясах.Ь встречались и структура, и переменная или функция с одним и тем же именем. Такие вопросы совместимости немаловажны, а для языковых пуристов это особо лакомый кусочек. К сожалению, пока не найдено удовлетворительное (и как правило, тривиальное) решение и данным вопросам приходится уделять слишком много внимания и сил. А когда решение найдено, проблема совместимости становится просто скучной, поскольку в ней нет никакой интеллектуальной ценности, а есть лишь голый практицизм.
Принятое в С++ решение проблемы множественных пространств имен в С состоит в том, что одним именем может обозначаться как класс, так и переменная или функция. Таким образом, имя относится ие к классу, если только оно явно не уточнено одним из ключевых слов вггисг, с1аяя или пп(оп. Споры с упрямыми старыми пользователями С, так называемыми экспертами, а также реальные проблемы совместимости с С были и остаются одним из самых трудных и вызывающих разочарование аспектов разработки С++. Язык С ялФ С!аззез БИИИИИИВ 2.8.3.
Важноаь синтаксиса Я полагаю, что большинство людей уделяет слишком много внимания синтаксису в ущерб вопросам контроля типов. Однако для дизайна С++ критичными всегда были проблемы типизации, недвусмысленности и контроля доступа, а вовсе не синтаксиса. Не хочу сказать, что синтаксис не имеет никакого значения. Напротив, он исключительно важен.
Удачный синтаксис помогает программисту при изучении новых концепций и позволяет избегать глупых ошибок, поскольку записать их труднее, чем их правильные альтернативы. Однако синтаксис надо проектировать так, чтобы оп соответствовал семантическим понятиям языка, а не наоборот. Отсюда следует, что центральной темой при обсуждении языка должен быть вопрос о том, что можно выразить, а не как это сделать.
Тонким нюансом в дискуссиях о совместимости с С было то, что опытные С-программисты давно свыклись с тем, как та или иная операция осуществлялась раньше, и потому были соверщенно нетерпимы к несовместимостям, которые требовались для поддержания такого стиля программирования, который в дизайне С не был заложен. И наоборот, програчмисты, не писавшие на С, обычно недооценивают значимость синтаксиса для С-программистов. 2.9. Производные классы Концепция производного класса — это адаптация префиксной нотации классов, принятой в Яшц!а, и уже поэтому она схожа с концепцией подкласса в Бща1!га!Ь.
Названия «производный» и «базовый» были выбраны потому, что я никак не мог запомнить, что есть зцЬ, а что — зцрег, и заметил, что такие сложности возникают не только у меня. Вот еше одно наблюдение: для многих тот факт, что подкласс обычно содержит больше информации, чем его суперкласс, кажется неестественным. Изобретая термины «производный» и «базовый», я отошел от своего принципа не вводить новых названий, если существуют старые.
В свою защиту скажу лишь, что я никогда не замечал путаницы по поводу этих терминов и что запомнить их очень просто даже тем, что не имеет математического образования. В С ъчгЬ С1эззез для концепции производного класса не было никакой поддержки во время исполнения. В частности, отсутствовало понятие виртуальной функции, которое было в Япщ!а, а позже вошло в С++.
Я просто сомневался (думаю, не без оснований) в своей способности научить людей ими пользоваться и, более того, в способности убедить, что для типичных применений виртуальная функция так же эффективна по скорости и по памяти, как обычная. Многие пользователи, имеющие опыт работы с Яшц!а или Бща!!га! Ь, до снх пор в это не верят, пока им не объяснишь подробно, как это сделано в С++, а некоторые и после испытывают сомнения. Даже и без виртуальных функций производные классы в С «г1гЬ С!аззез были полезны для построения новых структур данных из старых и для ассоциирования операций с получающимися типами.
Так, с их помощью удалось определить классы связанного списка и задачи (газк). НИИИ ИИИИ Производные классы 2.УЛ. Лолиморфизм без виртуальных функций Даже в отсутствие виртуальных функций пользователь мог работать с объектами производного класса, а устройство базового класса трактовать как деталь реализации. Например, если имеется класс вектора с элементами, проиндсксированными начиная с 0 и без проверки выхода за границы диапазона с1авв уессог ( /* */ фпс пег е1еп(ьпс 1)) ); с1авз вес : чессог ( тпс Ы, 1о; рцЫфс: /* */ пен(1пс 1о, тпс Ы); пес е1еп(1пс 1); ): ьпС нес.пег е1еп(сп (1 с 1о [[ Ы < 1) еггог["выход за границы диапазона"); гесцгп уессог.пес е1еп(1 - 1о); ) Можно было вместо этого ввести в базовый класс явное поле типа и использовать его совместно с явным приведением типов.
Первая стратегия применялась, когда пользователь видел только конкретные производные классы, а система видела все их базовые классы. Вторая стратегия полезна тогда, когда базовый класс реализовывал по сути дела одну вариантную зались для множества производных классов. Например, в работе (осгоцзсгцр, 1982Ъ) приведен следуюший уродливый код для извлечения объекта нз таблицы и использования имеюшегося в нем поля типа: с1авв е1еп ( /* свойства, хранящиеся в таблице */ ); с1авв саЫе ( /* табличные данные и функции поиска */ ); /* с1 папе — производный от е1еп */ /* ро папе - производный от е1ещ */ /* пав)зев - производный от саЫе */ с1авв с1 папе * с1; с1авв ро папе * ро; с1авв Павпеб * СаЫе; е1еп * р = саЫе->1оо)с["саггос") 1г (р) ( ви)ссп (р->суре) ( /* поле типа суре в объектах класса е1еп */ саве РО нанти ро = (с1авв ро папе *) р; /* явное преобразование типа */ то на его основе можно построить вектор элементов с индексами из заданного диапазона с контролем выхода за его границы: ПИИИИИИ6 Язык С )д/]Ф С!аваев Ьгеа)«г саве С( ндие: с1 = (с1аяя с1 папе *) рт /* явное преобразование типа */ Ьгеа)«т Г)Е Гап1С г еггог("неизвестный тип элемента"); ) е1ве еггог("саггог не определено" ); При проектировании С цг]гЬ С! аяяея и С++ было потрачено много усилий ради того, чтобы программисту не приходилось писать такой код.
2.9.2. Контейнерные классы без шаблонов В то время в моих программах и размышлениях важное место занимал вопрос о том, как с помощью комбинирования базовых классов, явных преобразований типов и (изредка) макросов можно получить обобщенные контейнерные классы. Например, в работе [Бггоцяггцр, 1982Ь1 продемонстрировано, как из списка объектов типа 11п)с построить список, который может содержать объекты одного типа: с1аяя ног«)1)п)г: 1)п)с ( сьаг иог«)(ЕХЕЕ]; рцЬ11с: уоЫ с1еаг(нот«))г с1аяя ногд11пх * аег(уо1«)); ( гесцгп (с1авя ног«)1)п)г *) 11п)г.дег(); ); уота рцс(с1авв ног«)1)п)с * р) ( 1)пй.рцс(р); ); )т «В примере класса я гас)г, упомянутом во введении, явно определялся стек символов.
Иногда это оказывается слишком частным случаем, А что, если нужен еще и стек длинных целыхт А если класс я гас)г предполагается использовать в библиотеке, когда тип элемента, помещаемого в стек, заранее неизвестен«В таких случаях объявление класса всас)г и ассоциированных с ним функций следует написать ток, чтобы при создании стека тип элементов можно было передать параметром так же, кок размер стека, Поскольку каждый объект 11п)с, который с помощью функции рпс ( ) помещается в список, должен принадлежать классу ыогс)11п]с, то обратное приведение к ьтогс)11п]с объекта типа 11п)с, извлекаемого из списка функцией пег (), безопасно.