|
Программирование >> Оптимизация возвращаемого значения
Глава 6. Разное Эта глава содержит информацию, которую по соображениям логичной организации материала нельзя было поместить ни в одну другую. Для начала поговорим о двух правилах объектно-ориентированной разработки программ на языке С++, которые помогут вам создавать системы, приспосабливающиеся к изменениям. Затем рассмотрим, как объединить в одной программе С и С++, что иногда бывает просто необходимо. И наконец, в последнем разделе книги описаны изменения в стандарте языка С++, произошедшие со времени публикации его фактического описания. Отдельное внимание уделено изменениям, которые были внесены в стандартную библиотеку. Если вы не следите за процессом стандартизации, здесь вас ожидает несколько приятных сюрпризов. Правило 32. Программируйте, заглядывая в будущее Все меняется. Не всегда известно, что именно изменится, как будут осуществляться эти изменения, когда они произойдут или почему, но точно ясно: все меняется. Хорошо написанные программы легко адаптируются к изменениям. Они воспринимают новые свойства, переносятся на новые платформы, подстраиваются под новые требования, обрабатывают новые разновидности входных данных. Гибкие, надежные и устойчивые программы не создаются случайно. Они разрабатываются и реализуются программистами, которые приспосабливаются к сегодняшним ограничениям, не забывая про нужды будущего. Программы, элегантно принимающие изменения, пишутся профессионалами, которые умеют заглядывать в будущее. Программировать с прицелом на будущее означает осознавать, что все меняется, и быть готовым к этому. Несомненно, к библиотекам будут добавляться новые функции, существующие функции станут снова перегружаться, и надо быть готовым к тому, что это может привести к потенциально двусмысленным вызовам функций. Вполне вероятно, в иерархиях появятся новые классы, а сегодняшние производные классы могут завтра стать базовыми. Постоянно будут писаться новые приложения, из-за чего функции будут вызываться в новом контексте, и ваша задача - заранее побеспокоиться о корректности их выполнения. Программисты, ответственные за поддержку ПО, обычно не являются разработчиками оригинального кода, следовательно, необходимо проектировать и реализовать программы так, чтобы облегчить их понимание, изменение и расширение другими. Один из способов сделать это - выражать ограничения разработки в самом коде, а не (или не только) в комментариях или другой документации. Например, если класс не должен иметь производных классов, не просто вставляйте комментарий в заголовочный файл класса, а предотвращайте наследование средствами С++ (см. правило 26). Если требуется, чтобы все экземпляры класса размещались в куче, не просто сообщайте об этом пользователям, а введите ограничение явно, применив подход, описанный в правиле 27. Если копирование и присваивание для класса не имеет смысла, запретите данные операции, объявив закрытыми конструктор копирования и оператор присваивания. C-i-i- - это мощный, гибкий и выразительный язык. Воспользуйтесь его свойствами, чтобы ввести в программы нужные ограничения. Поскольку все меняется, пишите классы, которые могут противостоять беспорядочной эволюции программного обеспечения. Избегайте делать функции виртуальными по требованию , то есть тогда, когда кто-то подойдет и попросит вас сделать это. Jljme определите значение функции и целесообразность ее переопределения в производных классах. Если такое переопределение имеет смысл, объявляйте ее как виртуальную, даже если пока это никому не нужно. В противном случае объявите ее невиртуальной и потом не меняйте своего решения просто из-за того, что кому-то так будет удобнее, не убедившись, что изменение имеет смысл в контексте всего класса и представляемой им абстракции. Включайте во все классы операторы присваивания и конструктор копирования, даже если никто никогда не будет их использовать . Их невостребованность здесь и сейчас не означает, что они не потребуются в будущем. Если реализовать эти функции сложно, объявите их как private. Тогда никто не сможет нечаянно вызывать функции, сгенерированные компилятором и делающие что-то не так (что часто происходит с операторами присваивания и конструкторами копирования, созданными по умолчанию). Придерживайтесь принципа минимальной новизны, то есть старайтесь создавать классы, операторы и функции, которые имеют естественный синтаксис и наглядную семантику. Сохраняйте согласованность со встроенными типами: если не знаете, как поступить, сделайте так же, как и для int. Помните: все, что можно сделать, пользователи сделают обязательно. Они будут генерировать исключения, присваивать объекты самим себе, использовать объекты перед присваиванием им значения, присваивать объектам значения и никогда к ним не обращаться, задавать слишком большие, слишком маленькие и нулевые значения. В общем, все, что может откомпилироваться, наверняка будет кем-то сделано. Поэтому программируйте такие классы, с которыми легко работать правильно и сложно - неправильно. Предполагайте, что пользователи будут делать ошибки, и проектируйте классы так, чтобы ошибки можно было предотвращать, обнаруживать или исправлять (см., например, правило 33). Старайтесь создавать переносимый код. Писать переносимые программы ненамного сложнее, чем непереносимые, и разница в производительности редко бывает достаточно существенной, чтобы оправдать применение непереносимых конструкций (см. правило 16). Даже программы, разработанные для оборудования, сделанного на заказ, часто потом переписываются как переносимые, так как обычно стандартное оборудование через несколько лет достигает такого же уровня производительности. Написание переносимых программ позволяет вам легко переходить с одной платформы на другую, расширять пользовательскую базу и хвастаться поддержкой открытых систем. Это тоже помогает легче наверстать упущенное, если вы поставили не на ту операционную систему. Разрабатывайте код так, чтобы влияние необходимых изменений было локализовано. Инкапсулируйте все, что можно, делайте детали реализации закрытыми. Почаще используйте неименованные пространства имен для статических в файле объектов и функций (см. правило 31). Пытайтесь избегать создания виртуальных базовых классов, поскольку такие классы должны инициализироваться во всех производных от них классов - даже косвенных производных (см. правило 4). Не применяйте без крайней нужды подход RTTI, при котором используются каскады операторов if-then-else (снова см. правило 31). Иногда при каждом изменении иерархии классов придется обновлять весь набор операторов, и если вы забудете хотя бы об одном из них, компиляторы не выдадут никакого предупреждения. Это призывы повторяются часто, но большинство программистов к ним не прислушиваются. К сожалению, и многие авторы тоже. Рассмотрим следующий совет известного эксперта по С++: Если кто-то удаляет объект В*, который указывает на D, то вам понадобится виртуальный деструктор . Здесь В является базовым классом, а D - производным. Другими словами, автор предполагает, что если ваша программа выглядит, как показано ниже, то в классе В вам не понадобится виртуальный деструктор: class В { . . . }; Виртуальный деструктор не нужен, class D: public В { ... } ; В *рЬ = new D; Но ситуация меняется, если добавить оператор: delete pb; Теперь вам нужен виртуальный деструктор в классе В. Небольшое изменение в пользовательском коде - добавление оператора delete - приводит к необходимости изменять определение класса В, а значит, все пользователи гсласса В должны будут выполнить перекомпиляцию. Если последовать совету процитированного автора, то добавление единственного оператора к одной функции может потребовать обширной перекомпиляции и перекомпоновки кода для всех пользователей данной библиотеки. Это какой угодно, только не эффективный подход к разработке программ. Другой автор пишет на ту же тему: Если открытый базовый класс не имеет виртуального деструктора, то ни производный класс, ни его члены не должны иметь деструкторов .
|
© 2006 - 2025 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |