Чтение онлайн

на главную

Жанры

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

Майерс Скотт

Шрифт:

Чтобы исправить ситуацию, нужно как-то заставить C++ отказаться от догмы «не заглядывай в шаблонные базовые классы». Добиться этого можно тремя способами. Во-первых, можно предварить обращения к функциям из базового класса указателем this:

template <typename Company>

class LoggingMsgSender: public MsgSender<Company> {

public:

...

void sendClearMsg(const MsgInfo& info)

{

записать в протокол перед
отправкой

;

this->sendClear(info); // порядок! Предполагается, что

// sendClear будет унаследована

записать в протокол после отправки

;

}

...

};

Во-вторых, можно воспользоваться using-объявлением. Мы уже обсуждали эту тему в правиле 33, где было сказано, что using-объявлением делает скрытые имена из базового класса видимыми в производном классе. Поэтому мы можем переписать sendClearMsg следующим образом:

template <typename Company>

class LoggingMsgSender: public MsgSender<Company> {

public:

using MsgSender<Company>::sendClear; // сообщает компилятору о том, что

... // sendClear есть в базовом классе

void sendClearMsg(const MsgInfo& info)

{

...

sendClear(info); // нормально, предполагается, что

... // sendClear будет унаследована

}

...

};

Хотя using-объявление будет работать как здесь, так и в правиле 33, но используются они для решения разных задач. Здесь проблема не в том, что имена из базового класса скрыты за именами, объявленными в производном классе, а в том, что компилятор вообще не станет производить поиск в области видимости базового класса, если только вы явно не попросите его об этом.

И последний способ заставить ваш код компилироваться – явно указать, что вызываемая функция находится в базовом классе:

template <typename Company>

class LoggingMsgSender: public MsgSender<Company> {

pubilc:

...

void sendClearMsg(const MsgInfo& info)

{

...

MsgSender<Company>::sendClear(info); // нормально, предполагается, что

... // sendClear будет унаследована

}

...

};

Но этот способ хуже прочих, посколько если вызываемая функция виртуальна, то явная квалификация отключает динамическое связывание.

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

подобного LoggingMsgSender, но если данное обещание не будет выполнено, истина всплывет позже. Например, если в программе есть такой код:

LoggingMsgSender<CompanyZ> zMsgSender;

MsgInfo msgData;

... // поместить info в msgData

zMsgSender.sendClearMsg(msgData); // ошибка! не скомпилируется

то вызов sendClearMsg не скомпилируется, потому что в этой точке компилятор знает, что базовый класс – это специализация шаблона MsgSender<CompanyZ> и в нем нет функции sendClear, которую sendClearMsg пытается вызвать.

Таким образом, суть дела в том, когда компилятор диагностирует неправильные обращения к членам базового класса – раньше (когда анализируются определения шаблонов производного класса) или позже (когда эти шаблоны конкретизируются переданными в шаблон аргументами). C++ предпочитает раннюю диагностику, и поэтому предполагает, что о содержимом базовых классов, конкретизируемых из шаблонов, не известно ничего.

Что следует помнить

• В шаблонах производных классов ссылки на имена из шаблонов базовых классов осуществляются с помощью префикса «this->», using-объявления либо посредством явного указания базового класса.

Правило 44: Размещайте независимый от параметров код вне шаблонов

Шаблоны – чудесный способ сэкономить время и избежать дублирования кода. Вместо того чтобы вводить код 20 похожих классов, в каждом из которых по 15 функций-членов, вы набираете текст одного шаблона и поручаете компилятору сгенерировать 20 конкретных классов и все 300 необходимых вам функций. (Функции-члены шаблонов классов неявно генерируются, только когда программа к ним обращается, поэтому все 300 функций-членов вы получите, лишь если будете все их использовать.) Шаблоны функций не менее привлекательны. Вместо написания множества однотипных функций вы пишете один шаблон и позволяете компиляторам проделать все остальное. Ну разве не восхитительная технология?

Да… иногда. Если вы не будете внимательны, то использование шаблонов может привести к разбуханию кода. Так называется дублирование в двоичной программе кода, данных или того и другого. В результате компактный и лаконичный исходный код в объектном виде становится громоздким и тяжелым. Хорошего в этом мало, поэтому нужно знать, как избежать такой неприятности.

Основной инструмент для этого – анализ общности и изменчивости имен, но в самой этой идее нет ничего необычного. Даже если вы никогда в жизни не писали шаблонов, таким анализом вам приходится заниматься постоянно.

Когда вы пишете функцию и обнаруживаете, что некоторая часть ее реализации мало чем отличается от реализации другой функции, разве вы дублируете код? Конечно, нет. Вы исключаете общую часть из обеих функций, помещаете ее в третью, а первые две вызывают эту третью функцию. Иными словами, вы анализируете эти две функции на предмет выявления общих и отличающихся частей, перемещаете общие части в новую функцию, а отличающиеся части оставляете на месте. Аналогично, если вы пишете класс и выясняется, что некоторые части этого класса в точности совпадают с частями другого класса, вы не станете их дублировать, а просто вынесете общие части в новый класс, а затем воспользуетесь наследованием или композицией (см. правила 32, 38 и 39), предоставив исходному классу доступ к общим средствам. Отличающиеся части исходных классов остаются на месте.

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

Воевода

Ланцов Михаил Алексеевич
5. Помещик
Фантастика:
альтернативная история
5.00
рейтинг книги
Воевода

Девятый

Каменистый Артем
1. Девятый
Фантастика:
боевая фантастика
попаданцы
9.15
рейтинг книги
Девятый

Совершенный: пробуждение

Vector
1. Совершенный
Фантастика:
боевая фантастика
рпг
5.00
рейтинг книги
Совершенный: пробуждение

Кодекс Крови. Книга Х

Борзых М.
10. РОС: Кодекс Крови
Фантастика:
фэнтези
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Кодекс Крови. Книга Х

Дайте поспать! Том IV

Матисов Павел
4. Вечный Сон
Фантастика:
городское фэнтези
постапокалипсис
рпг
5.00
рейтинг книги
Дайте поспать! Том IV

Ротмистр Гордеев

Дашко Дмитрий Николаевич
1. Ротмистр Гордеев
Фантастика:
фэнтези
попаданцы
альтернативная история
5.00
рейтинг книги
Ротмистр Гордеев

Как я строил магическую империю 2

Зубов Константин
2. Как я строил магическую империю
Фантастика:
попаданцы
аниме
5.00
рейтинг книги
Как я строил магическую империю 2

Тройняшки не по плану. Идеальный генофонд

Лесневская Вероника
Роковые подмены
Любовные романы:
современные любовные романы
6.80
рейтинг книги
Тройняшки не по плану. Идеальный генофонд

Специалист

Кораблев Родион
17. Другая сторона
Фантастика:
боевая фантастика
попаданцы
рпг
5.00
рейтинг книги
Специалист

Не грози Дубровскому! Том IX

Панарин Антон
9. РОС: Не грози Дубровскому!
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Не грози Дубровскому! Том IX

Неудержимый. Книга III

Боярский Андрей
3. Неудержимый
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Неудержимый. Книга III

Изгой. Пенталогия

Михайлов Дем Алексеевич
Изгой
Фантастика:
фэнтези
9.01
рейтинг книги
Изгой. Пенталогия

Жена по ошибке

Ардова Алиса
Любовные романы:
любовно-фантастические романы
7.71
рейтинг книги
Жена по ошибке

Пистоль и шпага

Дроздов Анатолий Федорович
2. Штуцер и тесак
Фантастика:
альтернативная история
8.28
рейтинг книги
Пистоль и шпага