Б. Страуструп - Дизайн и Эволюция C++. 2006 (1160775), страница 33
Текст из файла (страница 33)
До этих идеалов языку С далеко, как до небес. Любая глобальная функция или глобальная переменная видна компоновщику, поэтому ее имя будет конфликтовать с такими же именами в других частях программы, если только оно явно не объявлено как зс а с у с. Любое имя может использоваться как имя функции, даже КИИИИИИИ Правила проектирования языка С++ если оно не объявлено заранее. До сих пор имена структур, объявленных внутри структур, глобальны, это пережиток тех дней, когда глобальными были вообще все имена членов структур.
Помимо всего прочего, при обработке макросов препроцессор не принимает во внимание области действия, поэтому любая последовательность символов в программе может быть заменена чем-то иным, если модифицировать заголовочный файл или применить другой флаг компиляции (см. раздел 18.1).
Все это дает чрезвычайно мощные средства, если вы хотите изменить семантику локального, на первый взгляд, фрагмента кода или, наоборот, произвести существенную трансформацию путем маленькой «локальной» модификации. В целом это кардинально противоречит моим представлениям о создании и сопровождении сложных программных систем. Поэтому я поставил себе целью обеспечить лучшую защиту от «проникновений извне» и лучший контроль над тем, что «экспортируется» из моего кода.
Классы — первый и самый главный механизм локализации кода и предоставления доступа только через четко определенный интерфейс. Вложенные классы (см. разделы 3.12 и 13.5) и пространства имен (см. главу 17) расширяют понятие локальной области действия и явного предоставления доступа. В любом случае объем глобальной информации в системе резко уменьшается. Контроль доступа позволяет локализовать доступ без каких бы то ни было издержек во время исполнения, неизбежных при полном разъединении частей программы (см.
раздел 2.10). С помощью абстрактных классов можно еше больше отделить друг от друга разные части при минимальных затратах (см. раздел 13.2). Важно, что, оставаясь внутри класса или пространства имен, допустимо отделить объявление от реализации. Тогда проще разобраться в том, что класс делает, не углубляясь в изучение функций-членов, чтобы понять, как это делается. В объявлении класса допустимы встраиваемые функции, позволяющие добиться локальности в случаях, когда такое разделение неудобно. И наконец, код проще понять и изменить, когда большая его часть помещается на экране.
Здесь очень к месту традиционная краткость С, а возможность объявлять в С++ новые переменные непосредственно перед их использованием (см. раздел 3.11.5) — еще один шаг в том же направлении. Избегать зивисимосгли от порядка. Зависимость от порядка — причина путаницы и ошибок при реорганизации кода.
Все знают, что предложения выполняются в определенном порядке, но на зависимости между глобальными объявлениями и объявлениями членов класса часто не обращают внимания. Правила перегрузки (см. раздел 11.2) и правила использования базовых классов (см. раздел 12.2) специально составлены так, чтобы избежать данной зависимости.
В идеале ошибка должна выдаваться, когда к изменению семантики приводит перестановка двух объявлений. Для членов классов это верно, но для глобальных объявлений поддержать данное правило невозможно. Препроцессор С может ввести неожиданные и некорректные зависимости в результате макроподстановок, что приведет к полной неразберихе (см.
раздел 18.1). Ошибка на этапе компиляции все же лучше, чем непонятно как разрешенный конфликт. Характерный пример — правила разрешения неоднозначности для множественного наследования (см. раздел 12.2). Правила разрешения неоднозначности Технические правила НИИИИШКВ для перегруженных функций — пример того, как трудно добиться желаемого в условиях ограничений на совместимость и гибкость (см. раздел 11.2.2).
Если есть сомнения, нужно выбирать такой вариант средства, которому легче обучить. Применять данное правило трудно, поскольку могут быть аргументы в пользу логической красоты и в пользу того, чтобы не отступать от хорошо знакомого. Практическому использованию этого принципа можно научиться, если смотреть, насколько написанные тобой учебные и справочные руководства понятны читателям. Одна из целей — облегчить труд преподавателей и персонала службы технической поддержки. Необходимо помнить, что, как уже говорилось, программисты— неглупый народ; нельзя жертвовать важной функциональностью в угоду простоте. Синтаксис важен, хотя бывает и нелогичньги. Важно, чтобы система типов была логически последовательной, а семантика языка — ясной и четко определенной.
Роль синтаксиса вторична, и, наверное, программист может работать практически с любым синтаксисом. Однако с помошью синтаксиса язык взаимодействует с пользователем. Есть пользователи, которые бесконечно привержены определенным синтаксическим конструкциям и отстаивают свое мнение прямо-таки с фанатичным пылом. Я не надеюсь изменить эту ситуацию или ввести новые семантические понятия.
Поэтому синтаксис С++ подобран так, чтобы не задевать предрассудки программистов. Однако со временем он будет приведен к более рациональному и регулярному виду. Моей целью было устранить такие пережитки, как неявный хпс (см. раздел 2.8.1), старая форма приведения типов (см, раздел 14.3.1), и в то же время свести к минимуму использование усложненных форм синтаксиса объявлений (см. раздел 2.8.1). Опыт показывает, что люди привыкли к тому, что каждому новому понятию должно соответствовать новое ключевое слово, поэтому обучить новшеству, для которого ключевого слова нет, оказывается на удивление трудно.
Это явление имеет более глубокие корни, чем громко выражаемая нелюбовь к новым ключевым словам. Если предоставить человеку выбор и время для раздумий, то он обязательно примет решение в пользу нового ключевого слова, а не изобретательного обходного пути. Я стараюсь, чтобы важные операции были очень заметны. Например, одна из проблем, связанных со старым стилем приведения типов, состоит в том, что конструкции приведения почти не выделяются в тексте программы.
Кроме того, я предпочитаю, чтобы семантически неудачные операции, например плохие приведения типов, выглядели некрасиво и с синтаксической точки зрения (см. раздел 14.3.3). Обычно я выбираю лаконичный синтаксис. Использование нренроцессора должно быть исключено. Без препроцессора сам С, а за ним и С++ были бы чмертворожденными» языками. Без Срр они не стали бы достаточно выразительными и гибкими для использования в больших проектах. С другой стороны, именно из-за низкоуровневой семантики Срр так трудно н дорого создать более развитые и изяшно оформленные среды для программирования на С.
Поэтому было необходимо найти замены для всех сушественных возможностей Срр, которые бы гармонично сочетались с синтаксисом и семантикой С++. ИИИИИИН1 Правила проектирования языка С++ Сделав это, мы получили бы более дешевое и намного более удобное окружение для работы с С++. А заодно устранили бы источники многих трудных для обнаружения ошибок. Шаблоны (см. главу 15), встраиваемые функции (см.
раздел 2А.1), константы (см. раздел 3.8) и пространства имен (см, главу 17) — вот шаги в этом направлении. 4.5. Правила поддержки низкоуровневого программирования Разумеется, вышеупомянутые правила относятся практически ко всем особенностям языка.
А те, что приведены в таблице 4.5, применимы к С++ не только как к языку для системного программирования, но и как к средству для выражения высокоуровневого проектирования. Таблица 4.5 Правила поддержки низкоуровневого программирования Использовать традиционные компоновщики Никаких неоправданных несовместимостей с С Не оставлять места для языка более низкого уровня, чем С++, исключая ассемблер Правило нулевых издерявк: чем нв пользуетесь, за то не платите Если есть сомнения, предоставлять средства для ручного контроля Использовать традиционные компоновщики.
С самого начала были поставлены цели обеспечить простоту переносимости и взаимодействия с модулями, написанными на других языках. Ради этого пришлось настаивать патом, чтобы С++ можно было реализовать при использовании традиционных компоновщиков. Но работать с технологией, которая возникла еще во времена ранних версий Рогггап, нелегко. Некоторые особенности С++, прежде всего, безопасную компоновку (см. раздел 11.3) и шаблоны (см.
главу 15) можно, конечно, реализовать и с помощью традиционных компоновщиков, но при наличии более развитой поддержки реализация была бы лучше. Подспудно мы хотели, чтобы С++ дал стимул для разработки более совершенных компоновщиков. Традиционный компоновщик сравнительно легко позволяет обеспечить совместимость с С на уровне редактирования связей. Это важно для безболезненного использования средств операционной системы, библиотек, написанных на С, Гогггап и т.д., и для написания кода, который можно поместить в библиотеки, вызываемые из других языков.
Традиционный компоновщик важен и при написании программ, используемых на низких уровнях системы, например драйверов устройств. Никаких неоправданных несовместимостей с С. С вЂ” это язык системного программирования, получивший наибольшее признание. Программисты хорошо знают С, на нем написаны миллиарды строк кода, существует целая индустрия, специализирующаяся на предоставлении инструментов и услуг для этого языка. С++ основан на С.