Однако строка 33 заблокирована, так как она вызовет ошибку компиляции. Хотя логично было предположить, что в классе Dog свободно можно использовать метод Move(int), поскольку замещен был только метод Move(), но в действительности в данной ситуации, чтобы использовать Move(int), его также нужно заместить в классе Dog. В случае замещения одного из перегруженных методов скрытыми оказываются все варианты этого метода в базовом классе. Если вы хотите использовать в производном классе другие варианты перегруженного метода, то их также нужно заместить в этом классе.
Часто случается ошибка, когда после попытки заместить метод в производном классе данный метод оказывается недоступным для класса из-за того, что программист забыл установить ключевое слово const, используемое при объявлении метода в базовом классе. Вспомните, что слово const является частью сигнатуры, а несоответствие сигнатур ведет к скрытию базового метода, а не к его замещению.
Замещение и сокрытие
В следующем разделе главы будут рассматриваться виртуальные методы. Замещение виртуальных методов ведет к полиморфизму, а сокрытие методов разрушает поли- морфизм. Скоро вы узнаете об этом больше.
Даже если вы заместили базовый метод, то все равно можете обратиться к нему, указав базовый класс, где хранится исходное объявление метода. Для этого в обращении к методу нужно явно указать имя базового класса, за которым следуют два символа двоеточия и имя метода. Например: Mammal: :Move().
Если в листинге 11.6 переписать строку 32 так, как показано ниже, то ошибка во время компиляции больше возникать не будет:
32: fido.Mammal::Move();
Такая запись, реализованная в листинге 11.7, называется явным обрашением к методу базового класса.
Листинг 11.7. Явное обращение к методу базового класса
1: //Листинг 11.7. Явное обращение к методу базового класса
2:
3: #include
4:
5: class Mammal
6: {
7: public:
8: void Move() const { cout << "Mammal move one step\n"; }
9: void Move(int distance) const
10: {
11: cout << "Mammal move " << distance;
12: cout << " steps.\n";
13: }
14:
15: protected:
16: int itsAge;
17: int itsWeight;
18: };
19:
20: class Dog : public Mammal
21: {
22: public:
23: void Move()const;
24:
25: };
26:
27: void Dog::Move() const
28: {
29: cout << "In dog move...\n";
30: Mammal::Move(3);
31: }
32:
33: int main()
34: {
35: Mammal bigAnimal;
36: Dog fido;
37: bigAnimal.Move(2);
38: fido.Mammal::Move(6);
39: return 0;
40: }
Результат:
Mammal move 2 steps.
Mammal move 6 steps.
Анализ:В строке 35 создается объект bigAnimal класса Mammal, а в строке 36 — объект fido класса Dog. В строке 37 вызывается метод Move(int) из базового класса для объекта класса Dog.
В предыдущей версии программы мы столкнулись с проблемой из-за того, что в классе Dog доступен только один замещенный метод Move(), в котором не задаются параметры. Проблема была разрешена явным обращением к методу Move(int) базового класса в строке 38.
Рекомендуется: Повышайте функциональные возможности класса путем создания новых производных классов. Изменяйте выполнение отдельных функций в производных классах с помощью замещения методов.
Не рекомендуется: Не допускайте сокрытие функций базового класса из-за несоответствия сигнатур.
В этой главе неоднократно подчеркивалось, что объекты класса Dog одновременно являются объектами класса Mammal. До сих пор под этим подразумевалось, что объекты класса Dog наследуют все атрибуты (данные) и возможности (методы) базового класса. Но в языке C++ принципы иерархического построения классов несут в себе еще более глубинный смысл.
Полиморфизм в C++ развит настолько, что допускается присвоение указателям на базовый класс адресов объектов производных классов, как в следующем примере:
Mammal* pMammal = new Dog;
Данное выражение создает в области динамической памяти новый объект класса Dog и возвращает указатель на этот объект, который является указателем класса Mammal. Это вполне логично, так как собака — представитель млекопитающих.
Примечание: В этом суть полиморфизма. Например, можно объявить множество окон разных типов, включая диалоговые, прокручиваемые окна и поля списков, после чего создавать их в программе с помощью единственного виртуального метода draw(). Создав указатель на базовое окно и присваивая этому указателю адреса объектов производных классов, можно обращаться к методу draw() независимо от того, с каким из объектов в данный момент связан указатель. Причем всегда будет вызываться вариант метода, специфичный для класса выбранного объекта.
Читать дальше