Вход/Регистрация
Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ
вернуться

Майерс Скотт

Шрифт:
Что следует помнить

• Никогда не переопределяйте наследуемые невиртуальные функции.

Правило 37: Никогда не переопределяйте наследуемое значение аргумента функции по умолчанию

Давайте с самого начала упростим обсуждение. Есть только два типа функций, которые можно наследовать: виртуальные и невиртуальные. Но переопределять наследуемые невиртуальные функции в любом случае ошибочно (см. правило 36), поэтому мы вполне можем ограничить наше обсуждение случаем наследования виртуальной функции со значением аргумента по умолчанию.

В этих обстоятельствах мотивировка настоящего правила становится достаточно очевидной: виртуальные функции связываются динамически, а значения аргументов по умолчанию – статически.

Что это значит? Вы говорите, что уже позабыли, в чем заключается разница между статическим и динамическим связыванием? (Кстати, статическое связывание называют еще ранним связыванием, а динамическое – поздним.) Что ж, давайте освежим вашу память.

Статический тип объекта – это тип, объявленный вами в тексте программы. Рассмотрим следующую иерархию классов:

// классы для представления геометрических фигур

class Shape {

public:

enum ShapeColor { Red, Green, Blue };

// все фигуры должны предоставлять функцию для рисования

virtual void draw(ShapeColor color = Red) const = 0;

...

};

class Rectangle: public Shape {

public:

// заметьте, другое значение параметра по умолчанию – плохо!

virtual void draw(ShapeColor color = Green) const;

...

};

class Circle: public Shape {

public:

virtual void draw(ShapeColor color) const;

...

};

Графически это можно представить так:

Теперь рассмотрим следующие указатели:

Shape *ps; // статический тип – Shape*

Shape *pc = new Circle; // статический тип – Shape*

Shape *pr = new Rectangle; // статический тип – Shape*

В этом примере ps, pc и pr объявлены как указатели на Shape, так что для всех них он и будет выступать в роли статического типа. Отметим, что не совершенно безразлично, на что они указывают в действительности, – независимо от этого они имеют статический тип Shape*.

Динамический тип объекта определяется типом того объекта, на который он ссылается в данный момент. Иными словами, динамический тип определяет поведение объекта. В приведенном выше примере динамический тип pc – это Circle*, а динамический тип pr – Recangle*. Что касается ps, то он не имеет динамического типа, потому что не указывает ни на какой объект (пока).

Динамические типы, как следует из их названия, могут изменяться в процессе работы программы, обычно вследствие присваивания:

ps = pc; // динамический тип ps теперь Circle*

ps = pr; // динамический тип ps теперь Rectangle*

Виртуальные функции связываются динамически, то есть динамический тип вызывающего объекта определяет, какая конкретная функция вызывается:

pc->draw(Shape::Red); // вызывается Circle::draw(Shape::Red)

pr->draw(Shape::Red); // вызывается Rectangle::draw(Shape::Red)

Я знаю, что все это давно известно, и вы, несомненно, разбираетесь в виртуальных функциях. Самое интересное начинается, когда мы подходим к виртуальным функциям с аргументами, принимающими значения по умолчанию, поскольку, как я уже сказал, виртуальные функции связываются динамически, а аргументы по умолчанию – статически. Следовательно, вы можете прийти к тому, что будете вызывать виртуальную функцию, определенную в производном классе, но при этом использовать аргументы по умолчанию, заданные в базовом классе:

pr->draw; // вызывается Rectangle::draw(Shape::Red)!

В этом случае динамический тип pr – это Rectangle*, поэтому, как вы и ожидали, вызывается виртуальная функция класса Rectangle. Для функции Rectangle::draw значение аргумента по умолчанию – Green. Но поскольку статический тип pr – Shape*, то значения аргумента по умолчанию берутся из класса Shape, а не Rectangle! В результате получаем вызов, состоящий из странной, совершенно неожиданной комбинации объявлений draw из классов Shape и Rectangle.

  • Читать дальше
  • 1
  • ...
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: