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

Майерс Скотт

Шрифт:

class ModelA: public Airplane {

public:

virtual void fly(const Airport& destination)

{ defaultFly(destination};}

...

};

class ModelB: public Airplane {

public:

virtual void fly(const Airport& destination)

{ defaultFly(destination};}

...

};

Теперь для класса ModelC возможность случайно унаследовать некорректную реализацию fly исключена, поскольку чисто виртуальная функция в Airplane вынуждает ModelC создавать свою собственную версию fly.

class ModelC: public Airplane {

public:

virtual void fly(const Airport& destination)

...

};

void ModelC::fly(const Airport& destination)

{

код, описывающий полет самолета ModelC в заданный пункт назначения

}

Эта схема не обеспечивает «защиту от дурака» (программисты все же могут создать себе проблемы копированием/вставкой), но она более надежна, чем исходная. Что же касается функции Airplane::defaultFly, то она объявлена защищенной, поскольку действительно является деталью реализации класса Airplane и производных от него. Пассажиры теперь должны беспокоиться только о том, чтобы улететь, а не о том, как происходит полет.

Важно также то, что Airplane::defaultFly объявлена как невиртуальная функция. Это связано с тем, что никакой подкласс не должен ее переопределять – обстоятельство, которому посвящено правило 36. Если бы defaultFly была виртуальной, перед вами снова встала бы та же самая проблема: что, если некоторые подклассы забудут переопределить defaultFly должным образом?

Иногда высказываются возражения против идеи разделения функций на обеспечивающие интерфейс и реализацию по умолчанию, такие, например, как fly и defaultFly. Прежде всего, отмечают противники этой идеи, это засоряет пространство имен класса близкими названиями функций. Все же они соглашаются с тем, что интерфейс и реализация по умолчанию должны быть разделены. Как разрешить кажущееся противоречие? Для этого используется тот факт, что производные классы должны переопределять чисто виртуальные функции и при необходимости предоставлять свои собственные реализации. Вот как можно было бы использовать возможность определения чисто виртуальных функций в иерархии Airplane:

class Airplane {

public:

virtual void fly(const Airport& destination) = 0;

...

};

void Airplane::fly(const Airport& destination) // реализация чисто

{ // виртуальной функции

код по умолчанию, описывающий полет

самолета в заданный пункт назначения

}

class ModelA: pubic Airplane {

public:

virtual void fly(const Airport& destination)

{ Airplane::fly(destination);}

...

};

class ModelB: pubic Airplane {

public:

virtual void fly(const Airport& destination)

{ Airplane::fly(destination);}

...

};

class ModelC: pubic Airplane {

public:

virtual void fly(const Airport& destination);

...

};

void ModelC::fly(const Airport& destination)

{

код, описывающий полет самолета ModelC в заданный пункт назначения

}

Это практически такой же подход, как и прежде, за исключением того, что тело чисто виртуальной функции Airplane::fly заменяет собой независимую функцию Airplane::defaultFly. По существу, fly разбита на две основные составляющие. Объявление задает интерфейс (который должен быть использован в производных классах), а определение задает поведение по умолчанию (которое может использоваться производным классом, но только по явному требованию). Однако, производя слияние fly и defaultFly, мы теряем возможность задать для этих функций разные уровни доступа: код, который должен быть защищенным (функция defaultFly), становится открытым (потому что теперь он находится внутри fly).

И наконец, пришла очередь невиртуальной функции класса Shape – objectID:

class Shape {

public:

int objectID const;

...

};

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

  • Читать дальше
  • 1
  • ...
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • ...

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

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

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

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