Стандарты программирования на С++. 101 правило и рекомендация | страница 61



(бесполезно), б) копировать свои аргументы в объекты >super_string (расточительно) или в) преобразовать ссылки на >string в ссылки на >super_string (затруднительно и потенциально некорректно).

• Функции-члены >super_string не должны получить больший доступ к внутреннему устройству класса >string, чем свободные функции, поскольку класс >string, вероятно, не имеет защищенных (>protected) членов (вспомните — этот класс не предназначался для работы в качестве базового).

• Если класс >super_string скрывает некоторые из функций класса >string (а переопределение невиртуальных функций в производном классе не является перекрытием — это просто сокрытие), это может вызвать неразбериху в коде, работающем с объектами string, которые создаются автоматическим преобразованием из класса >super_string.

Словом, лучше добавлять новую функциональность посредством новых свободных (не являющихся членами) функций (см. рекомендацию 44). Чтобы избежать проблем поиска имен, убедитесь, что вы поместили функции в то же пространство имен, что и тип, для расширения функциональности которого они предназначены (см. рекомендацию 57). Некоторые программисты не любят свободные функции из-за их синтаксиса >Fun(str) вместо >str.Fun(), но это не более чем вопрос привычки.

Но что если класс >super_string наследуется из класса >string для добавления состояний, таких как кодировка или кэшированное значение количества слов? Открытое наследование не рекомендуется и в этом случае, поскольку класс >string не защищен от срезки (см. рекомендацию 54), и любое копирование >super_string в >string молча уберет все старательно хранимые дополнительные состояния.

И наконец, наследование класса с открытым невиртуальным деструктором рискует получить эффект неопределенного поведения при удалении указателя на объект типа >string, который на самом деле указывает на объект типа >super_string (см. рекомендацию 50). Это неопределенное поведение может даже оказаться вполне допустимым при использовании вашего компилятора и распределителя памяти, но оно все равно рано или поздно выявится в виде затаившихся ошибок, утечек памяти, разрушенной кучи и кошмаров переноса на другую платформу.

Примеры

Пример 1. Композиция вместо открытого или закрытого наследования. Что делать, если вам нужен тип >lосаlized_string, который "почти такой же, как и >string, но с дополнительными данными и функциями и небольшими переделками имеющихся функций-членов >string", и при этом реализация многих функций остается неизменной? В этом случае реализуйте ее с помощью класса