Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14 - [35]

Шрифт
Интервал

Следует запомнить

• Предпочитайте удаленные функции закрытым функциям без определений.

• Удаленной может быть любая функция, включая функции, не являющиеся членами, и инстанцирования шаблонов.

3.6. Объявляйте перекрывающие функции как >override

Мир объектно-ориентированного программирования С++ вращается вокруг классов, наследования и виртуальных функций. Среди наиболее фундаментальных идей этого мира — та, что реализации виртуальных функций в производных классах перекрывают (override) реализации их коллег в базовых классах. Понимание того, насколько легко все может пойти наперекосяк при перекрытии функций, попросту обескураживает. Полное ощущение, что эта часть языка создавалась как иллюстрация к законам Мерфи.

Очень часто термин “перекрытие” путают с термином “перегрузка”, хотя они совершенно не связаны друг с другом. Поэтому позвольте мне пояснить, что перекрытие виртуальной функции — это то, что делает возможным вызов функции производного класса через интерфейс базового класса:

>class Base {

>public:

> virtual void doWork();     // Виртуальная функция

>                            // базового класса

>};


>class Derived: public Base {

>public:

> virtual void doWork();      // Перекрывает Base::doWork

>                             // Ключевое слово virtual

>};                           // здесь не обязательное


>std::unique_ptr<Base> upb =  // Указатель на базовый класс

>std::make_unique<Derived>(); // указывает на объект

>                             // производного класса;

>                             // см. std::make_unique

>                             // в разделе 4.4


>upb->doWork();               // Вызов doWork через указатель

>                             // на базовый класс; вызывается

>                             // функция производного класса

Для осуществления перекрытия требуется выполнение нескольких условий.

• Функция базового класса должна быть виртуальной.

• Имена функций в базовом и производном классах должны быть одинаковыми (за исключением деструктора).

• Типы параметров функций в базовом и производном классах должны быть одинаковыми.

• Константность функций в базовом и производном классах должна совпадать.

• Возвращаемые типы и спецификации исключений функций в базовом и производном классах должны быть совместимыми.

К этим ограничениям, являющимся частью С++98, С++11 добавляет еще одно.

• Ссылочные квалификаторы функций должны быть идентичными. Ссылочные квалификаторы функции-члена являются одной из менее известных возможностей С++11, так что не удивляйтесь, если вы до сих пор ничего о них не слышали. Они позволяют ограничить использование функции-члена только объектами lvalue или только объектами rvalue. Для использования этих квалификаторов функции-члены не обязаны быть виртуальными.

>class Widget {

>public:

> …

> void doWork() &;      // Эта версия doWork применима, только

>                       // если *this представляет собой lvalue

> void doWork() &&;     // Эта версия doWork применима, только

>};                     // если *this представляет собой rvalue


>…

>Widget makeWidget();   // Фабричная функция (возвращает rvalue)

>Widget w;              // Обычный объект (lvalue)

>…

>w.doWork();            // Вызов Widget::doWork для lvalue

>                       // (т.e. Widget::doWork &)

>makeWidget().doWork(); // Вызов Widget::doWork для rvalue

>                       // (т.e. Widget::doWork &&)

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

Все эти требования к перекрытию означают, что маленькие ошибки могут привести к большим последствиям. Код, содержащий ошибки перекрытия, обычно корректен, но делает совсем те то, что хотел программист. Поэтому в данном случае нельзя полагаться на уведомления компиляторов о том, что вы что-то делаете неверно. Например, приведенный далее код является абсолютно законным и, на первый взгляд, выглядит разумным, но в нем нет перекрытия виртуальной функции — нет ни одной функции производного класса, связанной с функцией базового класса. Сможете ли вы самостоятельно определить, в чем заключается проблема в каждом конкретном случае (т.e. почему каждая функция производного класса не переопределяет функцию базового класса с тем же именем)?

>class Base {

>public:

> virtual void mf1() const;

> virtual void mf2(int x);

> virtual void mf3() &;

> void mf4() const;

>};


>class Derived: public Base {

>public:

> virtual void mf1();

> virtual void mf2(unsigned int x);

> virtual void mf3() &&;

> void mf4() const;

>};

Нужна помощь?

• >mf1 объявлена как >const в классе >Base, но в классе >Derived этого модификатора нет.

• >mf2 получает аргумент типа >int в классе >Base, но в классе >Derived она получает аргумент типа >unsigned int.

• >mf3 определена с квалификатором lvalue в классе >Base и с квалификатором rvalue в классе >Derived.

• >mf4 не объявлена в классе


Еще от автора Скотт Мейерс
Эффективное использование STL

В этой книге известный автор Скотт Мейерс раскрывает секреты настоящих мастеров, позволяющие добиться максимальной эффективности при работе с библиотекой STL.Во многих книгах описываются возможности STL, но только в этой рассказано о том, как работать с этой библиотекой. Каждый из 50 советов книги подкреплен анализом и убедительными примерами, поэтому читатель не только узнает, как решать ту или иную задачу, но и когда следует выбирать то или иное решение — и почему именно такое.


Как функции, не являющиеся методами, улучшают инкапсуляцию

Когда приходится инкапсулировать, то иногда лучше меньше, чем большеЯ начну со следующего утверждения: Если вы пишете функцию, которая может быть выполнена или как метод класса, или быть внешней по отношению к классу, Вы должны предпочесть ее реализацию без использования метода. Такое решение увеличивает инкапсуляцию класса. Когда Вы думаете об использовании инкапсуляции, Вы должны думать том, чтобы не использовать методы.Удивлены? Читайте дальше.


Рекомендуем почитать
Изучаем Java EE 7

Java Enterprise Edition (Java EE) остается одной из ведущих технологий и платформ на основе Java. Данная книга представляет собой логичное пошаговое руководство, в котором подробно описаны многие спецификации и эталонные реализации Java EE 7. Работа с ними продемонстрирована на практических примерах. В этом фундаментальном издании также используется новейшая версия инструмента GlassFish, предназначенного для развертывания и администрирования примеров кода. Книга написана ведущим специалистом по обработке запросов на спецификацию Java EE, членом наблюдательного совета организации Java Community Process (JCP)


Платформа J2Me

Эта книга научит вас, как разрабатывать программное обеспечение для платформы J2ME компании «Sun Microsystems». Эта книга придерживается стиля учебного пособия, это не справочное руководство.Цель — дать вам твердую основу в понятиях и техниках, которая даст вам возможность решиться на самостоятельную разработку качественных приложений.


Виртуальная библиотека Delphi

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Обработка баз данных на Visual Basic.NET

Это практическое руководство разработчика программного обеспечения на Visual Basic .NET и ADO.NET, предназначенное для создания приложений баз данных на основе WinForms, Web-форм и Web-служб. В книге описываются практические способы решения задач доступа к данным, с которыми сталкиваются разработчики на Visual Basic .NET в своей повседневной деятельности. Книга начинается с основных сведений о создании баз данных, использовании языка структурированных запросов SQL и системы управления базами данных Microsoft SQL Server 2000.


Исчерпывающее руководство по написанию всплывающих подсказок

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Программное обеспечение встроенных систем. Общие требования к разработке и документированию

Embedded system software. General requirements for development and documentationСтандарт подготовлен в развитие ГОСТ Р ИСО/МЭК 12207-99 «Информационная технология. Процессы жизненного цикла программных средств» с целью учета специфики разработки и документирования программного обеспечения встроенных систем реального времени.