переполнения буфера). Но и этот вариант относительно низкоуровневый, а потому так же открытый для ошибок, как и предыдущий (например, указанный файл может и не существовать).
istream
(или пользовательский класс File
, представляющий собой оболочку вокруг istream
, позволяющую устранить зависимость от реализации стандартной библиотеки) повышает уровень абстракции и делает API существенно менее проблематичным. Функция получает объект типа File
или соответствующий входной поток, она не должна заботиться об управлении памятью для строк, содержащих имя файла, и защищена от множества ошибок, которые вполне возможны при использовании первых двух вариантов. Остается только выполнить несколько проверок: файл должен быть открыт, а его содержимое иметь верный формат, но, в принципе, этим и ограничивается список неприятностей, которые могут произойти в данном варианте.
Даже если вы предпочтете воспользоваться во внешнем интерфейсе модуля низкоуровневой абстракцией, всегда используйте во внутренней реализации абстракции максимально высокого уровня и преобразуйте их в низкоуровневые абстракции на границах модуля. Например, если у вас имеются клиенты, не использующие С++, вы можете воспользоваться непрозрачным указателем void*
или дескриптором типа int
для работы с клиентом, но во внутренней реализации используйте высокоуровневые объекты. Преобразование между этими объектами и выбранными низкоуровневыми типами выполняйте только в интерфейсе модуля.
std::string Translate(const std::string&);
Для библиотек, используемых внутри одной команды компании, это обычно неплохое решение. Но если вы планируете динамически компоновать данный модуль с вызывающим кодом, который использует иную реализацию std::string
(например, иное размещение в памяти), то из-за такого несоответствия могут случиться разные странные и неприятные вещи.
Мы встречались с разработчиками, которые пытались использовать собственный класс-оболочку CustomString
для объектов std::string
, но в результате они сталкивались с той же проблемой, поскольку не имели полного контроля над процессом сборки всех клиентских приложений.
Одно из решений состоит в переходе к переносимым (вероятно, встроенным) типам, как вместо функции с аргументом string
, так и в дополнение к ней. Такой новой функцией может быть функция
void Translate(const char* src, char* dest, size_t destSize);
Использование низкоуровневой абстракции более переносимо, но всегда добавляет сложности; здесь, например, как вызывающий, так и вызываемый код должны явно использовать обрезку строки, если размера буфера оказывается недостаточно. (Заметим, что данная версия использует буфер, выделяемый вызывающим кодом, для того чтобы избежать ловушки, связанной с выделением и освобождением памяти в разных модулях — см. рекомендацию 60.)
Шаблоны и обобщенность
Место для вашей цитаты.
Аналогично: место для вашего введения.
В этом разделе мы считаем наиболее значимой рекомендацию 64 — 'Разумно сочетайте статический и динамический полиморфизм'.
64. Разумно сочетайте статический и динамический полиморфизм
Статический и динамический полиморфизм дополняют друг друга. Следует ясно представлять себе их преимущества и недостатки, чтобы использовать каждый из них там, где он дает наилучшие результаты, и сочетать их так, чтобы получить лучшее из обоих миров.
Динамический полиморфизм предстает перед нами в форме классов с виртуальными функциями и объектов, работа с которыми осуществляется косвенно — через указатели или ссылки. Статический полиморфизм включает шаблоны классов и функций.
Полиморфизм означает, что данное значение может иметь несколько типов, а данная функция может принимать аргументы типов, отличающихся от точных типов ее параметров. 'Полиморфизм представляет собой способ получить немного свободы динамической проверки типов, не теряя преимуществ статической проверки' — [Webber03].
Сила полиморфизма состоит в том, что один и тот же фрагмент кода может работать с разными типами, даже с теми, которые не были известны в момент написания этого кода. Такая 'применимость задним числом' является краеугольным камнем полиморфизма, поскольку существенно увеличивает пригодность и возможность повторного использования кода (см. рекомендацию 37). (В противоположность этому мономорфный код работает только со строго конкретными типами, теми, для работы с которыми он изначально создавался.)
Динамический полиморфизм позволяет значению иметь несколько типов посредством открытого наследования. Например, Derived* p
можно рассматривать как указатель не только на Derived
, но и на объект любого типа Base
, который прямо или косвенно является базовым для Derived
(свойство категоризации). Динамический полиморфизм известен также как включающий полиморфизм, поскольку множество, моделируемое Base
, включает специализации, моделируемые Derived
.
Благодаря своим характеристикам динамический полиморфизм в С++ наилучшим образом подходит для решения следующих задач.
• Employee
(Служащий), будет работать и с объектами Secretary
(Секретарь).
•
•