|
Программирование >> Полиморфизм без виртуальных функций в с++
если оно не объявлено заранее. До сих пор имена структур, объявленных внутри структур, глобальны, это пережиток тех дней, когда глобальными были вообще все имена членов структур. Помимо всего прочего, при обработке макросов препроцессор не принимает во внимание области действия, поэтому любая последовательность символов в программе может быть заменена чем-то иным, если модифицировать заголовочный файл или применить другой флаг компиляции (см. раздел 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). Если есть сомнения, нужно выбирать такой вариант средства, которому легче обучить. Применять данное правило трудно, поскольку могут быть аргументы в пользу логической красоты и в пользу того, чтобы не отступать от хорошо знакомого. Практическому использованию этого принципа можно научиться, если смотреть, насколько написанные тобой учебные и справочные руководства понятны читателям. Одна из целей - облегчить труд преподавателей и персонала службы технической поддержки. Необходимо помнить, что, как уже говорилось, программисты -неглупый народ; нельзя жертвовать важной функциональностью в угоду простоте. Синтаксис важен, хотя бывает и нелогичным. Важно, чтобы система типов была логически последовательной, а семантика языка - ясной и четко определенной. Роль синтаксиса вторична, и, наверное, программист .может работать практически с любым синтаксисо.м. Однако с помощью синтаксиса язык взаимодействует с пользователем. Есть пользователи, которые бесконечно привержены определенным синтаксическим конструкциям и отстаивают свое мнение прямо-таки с фанатичным пылом. Я не надеюсь изменить эту ситуацию или ввести новые семантические понятия. Поэтому синтаксис С++ подобран так, чтобы не задевать предрассудки програ.ммис-тов. Однако со временем он будет приведен к более рациональному и регулярному виду. Моей целью было устранить такие пережитки, как неявный int (см. раздел 2.8.1), старая форма приведения типов (см. раздел 14.3.1), и в то же время свести к минимуму использование усложненных форм синтаксиса объявлений (см. раздел 2.8.1). Опыт показывает, что люди привыкли к тому, что каждому новому понятию должно соответствовать новое ключевое слово, поэтому обучить новшеству, для которого ключевого слова нет, оказьшается на удивление трудно. Это явление имеет более глубокие корни, чем громко выражаемая нелюбовь к новы.м ключевым словам. Если предоставить человеку выбор и вре.мя для раздумий, то он обязательно примет решение в пользу нового ключевого слова, а не изобретательного обходного пути. Я стараюсь, чтобы важные операции были очень заметны. Например, одна из проблем, связанных со старым стилем приведения типов, состоит в том, что кон-сфукции приведения почти не выделяются в тексте программы. Кроме того, я предпочитаю, чтобы семантически неудачные операции, напри.мер плохие приведения типов, выглядели некрасиво и с синтаксической точки зрения (см. раздел 14.3.3). Обычно я выбираю лаконичный синтаксис. Использование препроцессора должно быть исключено. Без препроцессора сам С, аза ним и С++ были бы мертворожденными языками. Без Срр они не стали бы достаточно выразительными и гибкими для использования в больших проектах. С другой стороны, именно из-за низкоуровневой семантики Срр так трудно и дорого создать более развитые и изящно оформленные среды для програ.ммиро-вания на С. Поэтому было необходимо найти замены для всех существенных возможностей Срр, которые бы гармонично сочетались с синтаксисом и семантикой С++. Сделав это, мы получили бы более дешевое и намного более удобное окружение для работы с С++. А заодно устранили бы источники многих трудных для обнаружения ошибок. Шаблоны (см. главу 15), встраиваемые функции (см. раздел 2.4.1), константы (см. раздел 3.8) и пространства имен (см. главу 17) - вот шаги в этом направлении. 4.5. Правила поддержки низкоуровневого программирования Разумеется, вышеупомянутые правила относятся практически ко всем особенностям языка. А те, что приведены в таблице 4.5, применимы к С++ не только как к языку для системного программирования, но и как к средству для выражения высокоуровневого проектирования. Таблица 4.5 Правила поддержки низкоуровневого программирования Использовать традиционные компоновщики Никаких неоправданных несовместимостей с С Не оставлять места для языка более низкого уровня, чем С++, исключая ассемблер Правило нулевых издержек: чем не пользуетесь, за то не платите Если есть сомнения, предоставлять средства для ручного контроля Использовать традиционные компоновщики. С самого начала были поставлены цели обеспечить простоту переносимости и взаимодействия с модулями, написанными на других языках. Ради этого пришлось настаивать на том, чтобы С++ можно было реализовать при использовании традиционных компоновщиков. Но работать с технологией, которая возникла еще во времена ранних версий Fortran, нелегко. Некоторые особенности С++, прежде всего, безопасную компоновку (см. раздел 11.3) и шаблоны (см. главу 15) можно, конечно, реализовать и с помощью традиционных компоновщиков, но при наличии более развитой поддержки реализация была бы лучше. Подспудно мы хотели, чтобы С++ дал стимул для разработки более совершенных компоновщиков. Традиционный компоновщик сравнительно легко позволяет обеспечить совместимость с С на уровне редактирования связей. Это важно для безболезненного использования средств операционной системы, библиотек, написанных на С, Fortran и т.д., и для написания кода, который можно поместить в библиотеки, вызываемые из других языков. Традиционный компоновщик важен и при написании программ, используемых на низких уровнях системы, например драйверов устройств. Никаких неоправданных несовместимостей с С. С - это язык системного программирования, получивший наибольшее признание. Программисты хорошо знают С, на нем написаны миллиарды строк кода, существует целая индустрия, специализирующаяся на предоставлении инструментов и услуг для этого языка. С++ основан на С. Возникает вопрос: Насколько близки должны быть определения
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |