написание хороших программ более простым и приятным для отдельного
программиста. Плана разработки C++ на бумаге никогда не было;
проект, документация и реализация двигались одновременно.
Разумется, внешний интерфейс C++ был написан на C++. Никогда не
существовало "Проекта C++" и "Комитета по разработке C++". Поэтому
C++ развивался и продолжает развиваться во всех направлениях чтобы
справляться со сложностями, с которыми сталкиваются пользователи, а
также в процессе дискуссий автора с его друзьями и коллегами.
В качестве базового языка для C++ был выбран C, потому что он (1)
многоцелевой, лаконичный и относительно низкого уровня; (2)
отвечает большинству задач системного программирования; (3) идет
везде и на всем; и (4) пригоден в среде программирования UNIX. В C
есть свои сложности, но в наспех спроектированном языке тоже были
бы свои, а сложности C нам известны. Самое главное, работа с C
позволила "C с Классами" быть полезным (правда, неудобным)
инструментом в ходе первых месяцев раздумий о добавлении к C
Simula-образных классов.
C++ стал использоваться шире, и по мере того, как возможности,
предоставляемые им помимо возможностей C, становились все более
существенными, вновь и вновь поднимался вопрос о том, сохранять ли
совместимость с C. Ясно, что отказавшись от определенной части
наследия C можно было бы избежать ряда проблем (см., например,
Сэти [12]). Это не было сделано, потому что (1) есть миллионы строк
на C, которые могли бы принести пользу в C++ при условии, что их не
нужно было бы полностью переписывать с C на C++; (2) есть сотни
тысяч строк библиотечных функций и сервисных программ, написанных
на C, которые можно было бы использовать из или на C++ при условии,
что C++ полностью совместим с C по загрузке и синтаксически очень
похож на C; (3) есть десятки тысяч программистов, которые знают C,
и которым, поэтому, нужно только научиться использовать новые
особенности C++, а не заново изучать его основы; и (4), поскольку
C++ и C будут использоваться на одних и тех же системах одними и
теми же людьми, отличия должны быть либо очень большими, либо очень
маленькими, чтобы свести к минимуму ошибки и недоразумения. Позднее
была проведена проверка определения C++, чтобы удостовериться в
том, что любая конструкциия, допустимая и в C и в C++,
действительно означает в обоих языках одно и то же.
Язык C сам эволюционировал за последние несколько лет, частично
под влиянием развития C++ (см. Ростлер [11]). Предварительный
грубый ANSI стандарт C [10] содержит синтаксис описаний функций,
заимствованный из "C с Классами". Заимствование идей идет в обе
стороны; например, указатель void* был придуман для ANSI C и
- стр 14 -
впервые реализован в C++. Когда ANSI стандарт разовьется несколько
дальше, придет время пересмотреть C++, чтобы удалить необоснованную
несовместимость. Будет, например, модернизирован препроцессор
(#с.11), и нужно будет, вероятно, отрегулировать правила
осуществления плавающей арифметики. Это не должно оказаться
болезненным, и C и ANSI C очень близки к тому, чтобы стать
подмножествами C++ (см. #с.11).
Эффективность и структура
C++ был развит из языка программирования C и за очень немногими
исключениями сохраняет C как подмножество. Базовый язык, C
подмножество C++, спроектирован так, что имеется очень близкое
соответствие между его типами, операциями и операторами и
компьютерными объектами, с которыми непосредственно приходится
иметь дело: числами, символами и адресами. За исключением операций
свободной памяти new и delete, отдельные выражения и операторы C++
обычно не нуждаются в скрытой поддержке во время выполнения или
подпрограммах.
В C++ используются те же последовательности вызова и возврата из
функций, что и в C. В тех случаях, когда даже этот довольно
эффективный механизм является слишком дорогим, C++ функция может
быть подставлена inline, удовлетворяя, таким образом, соглашению о
записи функций без дополнительных расходов времени выполнения.
Одним из первоначальных предназначений C было применение его
вместо программирования на ассемблере в самых насущных задачах
системного программирования. Когда проектировался C++, были приняты
меры, чтобы не ставить под угрозу успехи в этой области. Различие
между C и C++ состоит в первую очередь в степени внимания,
уделяемого типам и структурам. C выразителен и снисходителен. C++
еще более выразителен, но чтобы достичь этой выразительности,
программист должен уделить больше внимания типам объектов. Когда
известны типы объектов, компилятор может правильно обрабатывать
выражения, тогда как в противном случае программисту пришлось бы
задавать действия с мучительными подробностями. Знание типов
объектов также позволяет компилятору обнаруживать ошибки, которые в
противном случае остались бы до тестирования. Заметьте, что
использование системы типов для того, чтобы получить проверку
параметров функций, защитить данные от случайного искажения, задать
новые операции и т.д., само по себе не увеличивает расходов по
времени выполнения и памяти.
Особое внимание, уделенное при разработке C++ структуре,
отразилось на возрастании масштаба программ, написанных со времени
разработки C. Маленькую программу (меньше 1000 строк) вы можете
заставить работать с помощью грубой силы, даже нарушая все правила
хорошего стиля. Для программ больших размеров это не совсем так.
Если программа в 10 000 строк имеет плохую структуру, то вы
обнаружите, что новые ошибки появляются так же быстро, как
удаляются старые. C++ был разработан так, чтобы дать возможность
разумным образом структурировать большие программы таким образом,
чтобы для одного человека не было непомерным справляться с
программами в 25 000 строк. Существуют программы гораздо больших
размеров, однако те, которые работают, в целом, как оказывается,
состоят из большого числа почти независимых частей, каждая из
которых намного ниже указанных пределов. Естественно, сложность
- стр 15 -
написания и поддержки программы зависит от сложности разработки, а
не просто от числа строк текста программы, так что точные цифры, с
помощью которых были выражены предыдущие соображения, не следует
воспринимать слишком серьезно.
Не каждая часть программы, однако, может быть хорошо
структурирована, независима от аппаратного обеспечения, легко
читаема и т.п. C++ обладает возможностями, предназначенные для
того, чтобы непосредственно и эффективно работать с аппаратными
средствами, не беспокоясь о безопасности или простоте понимания. Он
также имеет возможности, позволяющие скрывать такие программы за
элегантными и надежными интерфейсами.
В этой книге особый акцент делается на методах создания
универсальных средств, полезных типов, библиотек и т.д. Эти
средства пригодятся как тем программистам, которые пишут небольшие
программы, так и тем, которые пишут большие. Кроме того, поскольку
все нетривиальные программы состоят из большого числа
полунезависимых частей, методы написания таких частей пригодятся и
системным, и прикладным программистам.
У кого-то может появиться подозрение, что спецификация программы
с помощью более подробной системы типов приведет к увеличению
исходных текстов программы. В C++ это не так; C++ программа,
описывающая типы параметров функций, использующая классы и т.д.,
обычно немного короче эквивалентной C программы, в которой эти
средства не используются.
Философские замечания
Язык программирования служит двум связанным между собой целям: он
дает программисту аппарат для задания действий, которые должны быть
выполнены, и формирует концепции, которыми пользуется программист,
размышляя о том, что делать. Первой цели идеально отвечает язык,
который настолько "близок к машине", что всеми основными машинными
аспектами можно легко и просто оперировать достаточно очевидным для
программиста образом. С таким умыслом первоначально задумывался C.
Второй цели идеально отвечает язык, который настолько "близок к
решаемой задаче", чтобы концепции ее решения можно было выражать
прямо и коротко. С таким умыслом предварительно задумывались
средства, добавленные к C для создания C++.
Связь между языком, на котором мы думаем/программируем, и
задачами и решениями, которые мы можем представлять в своем
воображении, очень близка. По этой причине ограничивать свойства
языка только целями исключения ошибок программиста в лучшем случае
опасно. Как и в случае с естественными языками, есть огромная
польза быть по крайней мере двуязычным. Язык предоставляет
программисту набор концептуальных инструментов; если они не
отвечают задаче, то их просто игнорируют. Например, серьезные
ограничения концепции указателя заставляют программиста применять
вектора и целую арифметику, чтобы реализовать структуры, указатели
и т.п. Хорошее проектирование и отсутствие ошибок не может
гарантироваться чисто за счет языковых средств.
Система типов должна быть особенно полезна в нетривиальных
задачах. Действительно, концепция классов в C++ показала себя
мощным концептуальным средством.
- стр 16 -
Размышления о программировании на C++
В идеальном случае подход к разработке программы делится на три
части: вначале получить ясное понимание задачи, потом выделить
ключевые идеи, входящие в ее решение, и наконец выразить решение в
виде программы. Однако подробности задачи и идеи решения часто
становятся ясны только в результате попытки выразить их в виде
программы - именно в этом случае имеет значение выбор языка
программирования.
В большинстве разработок имеются понятия, которые трудно
представить в программе в виде одного из основных типов или как
функцию без ассоциированных с ней статических данных. Если имеется
подобное понятие, опишите класс, представляющий его в программе.
Класс - это тип; это значит, что он задает поведение объектов его
класса: как они создаются, как может осуществляться работа с ними,
и как они уничтожаются. Класс также задает способ представления
объектов; но на ранних стадиях разработки программы это не является
(не должно являться) главной заботой. Ключом к написанию хорошей
программы является разработка таких классов, чтобы каждый из них
представлял одно основное понятие. Обычно это означает, что
программист должен сосредоточиться на вопросах: Как создаются
объекты этого класса? Могут ли эти объекты копироваться и/или
уничтожаться? Какие действия можно производить над этими объектами?
Если на такие вопросы нет удовлетворительных ответов, то во-первых,
скорее всего, понятие не было "ясно", и может быть неплохо еще
немного подумать над задачей и предлагаемым решением вместо того,
чтобы сразу начинать "программировать вокруг" сложностей.
Проще всего иметь дело с такими понятиями, которые имеют
традиционную математическую форму: числа всех видов, множества,
геометрические фигуры и т.п. На самом деле, следовало бы иметь
стандартные библиотеки классов, представляющих такие понятия, но к
моменту написания это не имело места. C++ еще молод, и его
библиотеки не развились еще до той же степени, что и сам язык.
Понятие не существует в пустоте, всегда есть группы связанных
между собой понятий. Организовать в программе взаимоотношения между