Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ
Шрифт:
Этот
Как видите, это касается даже тех случаев, когда функции в базовом и производном классах принимают параметры разных типов, независимо от того, идет ли речь о виртуальных или невиртуальных функциях. И точно так же, как в нашем первом примере double x внутри функции someFunc скрывает int x из глобального контекста, так и здесь функция mf3 в классе Derived скрывает функцию mf3 из класса Base, которая имеет другой тип.
Обоснование такого поведения в том, что оно не дает нечаянно унаследовать перегруженные функции из базового класса, расположенного много выше в иерархии наследования, упрятанной в библиотеке или каркасе приложения. К сожалению, обычно вы хотите унаследовать перегруженные функции. Фактически если вы используете открытое наследование и не наследуете перегруженные функций, то нарушаете семантику отношения «является» между базовым и производным классами, которое в правиле 32 провозглашено фундаментальным принципом открытого наследования. То есть это тот случай, когда вы почти всегда хотите обойти принятое в C++ по умолчанию правило сокрытия имен.
Это можно сделать с помощью using-объявлений:
Теперь наследование будет работать, как и ожидается.
Это означает, что если вы наследуете базовому классу с перегруженными функциями и хотите переопределить только некоторые из них, то должны включить using-объявление для каждого имени, иначе оно будет скрыто.
Можно представить себе ситуацию, когда вы не хотите наследовать все функции из базовых классов. При открытом наследовании такое никогда не должно происходить, так как это противоречит смыслу отношения «является» между базовым классом и производным от него. Вот почему using-объявление находится в секции public объявления производного класса; имена, которые открыты в базовом классе, должны оставаться открытыми и в открыто унаследованном от него. Но при закрытом наследовании (см. правило 39) такое желание иногда осмыслено. Например, предположим, что класс Derived закрыто наследует классу Base, и единственная версия mfl, которую Derived хочет унаследовать, – это та, что не принимает параметров. Using-объявление в этом случае не поможет, поскольку оно делает видимыми в производном классе все унаследованные функции с заданным именем. Здесь требуется другая техника – простая перенаправляющая функция:
Другое применение встроенных перенаправляющих функций – обойти дефект в тех устаревших компиляторах, которые не поддерживают using-объявления для импорта унаследованных имен в область видимости производного класса.
Это все, что можно сказать о наследовании и сокрытии имен. Впрочем, когда наследование сочетается с шаблонами, возникает совсем другой вариант проблемы «сокрытия унаследованных имен». Все подробности, касающиеся шаблонов, см. в правиле 43.