17.4. Конструирование базового и производного классов
17.4. Конструирование базового и производного классов
Напомним, что объект производного класса состоит из одного или более подобъектов, соответствующих базовым классам, и части, относящейся к самому производному. Например, NameQuery состоит из подобъекта Query и объекта-члена string. Для иллюстрации поведения конструктора производного класса введем еще один член встроенного типа:
class NameQuery : public Query {
public:
// ...
protected:
bool _present;
string _name;
};
Если _present установлен в false, то слово _name в тексте отсутствует.
Рассмотрим случай, когда в NameQuery конструктор не определен. Тогда при определении объекта этого класса
NameQuery nq;
по очереди вызывается конструктор по умолчанию Query, а затем конструктор по умолчанию класса string (ассоциированный с объектом _name). Член _present остается неинициализированным, что потенциально может служить источником ошибок. Чтобы инициализировать его, можно так определить конструктор по умолчанию для класса NameQuery:
inline NameQuery::NameQuery() { _present = false; }
Теперь при определении nq вызываются три конструктора по умолчанию: для базового класса Query, для класса string при инициализации члена _name и для класса NameQuery.
А как передать аргумент конструктору базового класса Query? Ответить на этот вопрос можно, рассуждая по аналогии.
Для передачи одного или более аргументов конструктору объекта-члена мы используем список инициализации членов (здесь можно также задать начальные значения членам, не являющимся объектами классов; подробности см. в разделе 14.5):
inline NameQuery::
NameQuery( const string &name )
: _name( name ), _present( false )
{}
Для передачи одного или более аргументов конструктору базового класса также разрешается использовать список инициализации членов. В следующем примере мы передаем конструктору string аргумент name, а конструктору базового класса Query - объект, адресованный указателем ploc:
inline NameQuery::
NameQuery( const string &name,
vectorlocation *ploc )
: _name( name ), Query( *ploc ), _present( true )
{}
Хотя Query помещен в список инициализации вторым, его конструктор всегда вызывается раньше конструктора для _name. Порядок их вызова следующий:
Конструктор базового класса. Если базовых классов несколько, то конструкторы вызываются в порядке их следования в списке базовых классов, а не в порядке появления в списке инициализации. (О множественном наследовании в этой связи мы поговорим в главе 18.)
Конструктор объекта-члена. Если в классе есть несколько таких членов, то конструкторы вызываются в порядке их объявления в классе, а не в порядке появления в списке инициализации (подробнее см. раздел 14.5).
Конструктор производного класса.
Конструктор производного класса должен стремиться передать значение члена базового класса подходящему конструктору того же класса, а не присваивать его напрямую. В противном случае реализации двух классов становятся сильно связанными и тогда изменить или расширить реализацию базового будет затруднительно. (Ответственность разработчика базового класса ограничивается предоставлением подходящего множества конструкторов.)
В оставшейся части этого раздела мы последовательно изучим конструктор базового класса и конструкторы четырех производных от него, а после этого рассмотрим альтернативный дизайн иерархии классов Query, чтобы познакомиться с иерархиями глубиной больше двух. В конце раздела речь пойдет о деструкторах классов.
17.4.1. Конструктор базового класса
В нашем базовом классе объявлено два нестатических члена: _solution и _loc:
class Query {
public:
// ...
protected:
setshort *_solution;
vectorlocation _loc;
// ...
};
Конструктор Query по умолчанию должен явно инициализировать только член _solution. Для инициализации _loc автоматически вызывается конструктор класса vector. Вот реализация нашего конструктора:
inline Query::Query(): _solution( 0 ) {}
В Query нам понадобится еще один конструктор, принимающий ссылку на вектор позиций:
inline
Query::
Query( const vector locaton &loc )
: _solution( 0 ), _loc( loc )
{}
Он вызывается только из конструктора NameQuery, когда объект этого класса используется для представления указанного в запросе слова. В таком случае передается предварительно подготовленный для него вектор позиций. Остальные три производных класса вычисляют свои векторы позиций в соответствующей функции-члене eval(). (В следующем подразделе мы покажем, как это делается. Реализации функций-членов eval() приведены в разделе 17.5.)
Какой уровень доступа обеспечить для конструкторов? Мы не хотим объявлять их открытыми, так как предполагается, что Query будет существовать в программе только в виде подобъекта в составе объектов производных от него классов. Поэтому мы объявим конструктор не открытым, а защищенным:
class Query {
public:
// ...
protected:
Query();
// ...
};
Ко второму конструктору класса Query предъявляются еще более жесткие требования: он не только должен конструировать Query в виде подобъекта производного класса, но этот производный класс должен к тому же быть NameQuery. Можно объявить конструктор закрытым, а NameQuery сделать другом класса Query. (В предыдущем разделе мы говорили, что производный класс может получить доступ только к открытым и защищенным членам базового. Поэтому любая попытка вызвать второй конструктор из классов AndQuery, OrQuery или NotQuery приведет к ошибке компиляции.)
class Query {
public:
// ...
protected:
Query();
// ...
private:
explicit Query( const vectorlocation& );
};
(Необходимость второго конструктора спорна; вероятно, правильнее заполнить _loc в функции eval() класса NameQuery. Однако принятый подход в большей степени отвечает нашей цели проиллюстрировать использование конструктора базового класса.)
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
Глава 6 Конструирование форм
Глава 6 Конструирование форм 6.1. Элемент FORM6.2. Создание полей формыЧто главное в Интернете? Его интерактивность. Можно общаться с посетителями сайта и получать от них отзывы о работе – такое взаимодействие всегда полезно и интересно. Создатели сайта узнают, насколько он
Конструирование модулей
Конструирование модулей Введение Эта обучающая серия обещает стать возможно одной из самых долгоиграющих мини-серий в истории, конкурирующей только с задержкой на Томе IV Кнута. Начатая в 1988, эта серия вошла в четырехлетнюю паузу в 1990, когда «заботы мира сего», изменения
Шаг 11 - Нетривиальное конструирование объектов.
Шаг 11 - Нетривиальное конструирование объектов. В прошлом шаге мы уже столкнулись с ситуацией, когда явное конструирование объектов нежелательно. Выход в таком случае - убрать конструкторы из открытой части объявления. Возможны два варианта - если Вы планируете
17.2.1. Определение базового класса
17.2.1. Определение базового класса * Члены Query представляют: множество операций, поддерживаемых всеми производными от него классами запросов. Сюда входят как виртуальные операции, переопределяемые в производных классах, так и невиртуальные, разделяемые всеми производными
17.4.1. Конструктор базового класса
17.4.1. Конструктор базового класса В нашем базовом классе объявлено два нестатических члена: _solution и _loc:class Query {public:// ...protected:setshort *_solution;vectorlocation _loc;// ...};Конструктор Query по умолчанию должен явно инициализировать только член _solution. Для инициализации _loc автоматически вызывается
17.4.2. Конструктор производного класса
17.4.2. Конструктор производного класса В классе NameQuery также определены два конструктора. Они объявлены открытыми, поскольку ожидается, что в приложении будут создаваться объекты этого класса:class NameQuery : public Query {public:explicit NameQuery( const string& );NameQuery( const string&, const vector* );// ...protected://
18.5.1. Объявление виртуального базового класса
18.5.1. Объявление виртуального базового класса Для указания виртуального наследования в объявление базового класса вставляется модификатор virtual. Так, в данном примере ZooAnimal становится виртуальным базовым для Bear и Raccoon:// взаимное расположение ключевых слов public и virtual//
Конструирование Splitter
Конструирование Splitter У меня есть форма с расположенными на ней компонентами TreeView и Memo. Значение свойства align обоих компонентов позволяет им занимать всю форму. Я хотел бы расположить между ними движок типа Splitter, пропорционально меняющий их размеры (один шире, другой
Конструирование макетов
Конструирование макетов Данный режим служит для построения макетов корпусной мебели (обычных кухонных шкафов).Выберите в раскрывающемся списке Раздел пункт Конструирование макетов и нажмите кнопку ОК – откроется соответствующее окно (рис. 7.2). Рис. 7.2. Окно
Переопределение методов базового класса
Переопределение методов базового класса В порожденном классе можно определить методы и элементы данных с именами, которые уже используются в базовом классе. Соответствующие методы и элементы данных базового класса оказываются скрыты. Чтобы обратиться к ним, необходимо
Классы, не имеющие базового класса
Классы, не имеющие базового класса Кроме классов, наследованных от базового класса CObject, библиотека MFC включает ряд самостоятельных классов. У них нет общего базового класса и они имеют различное назначение.Несколько классов, которые не наследуются от базового класса
7.2.1 Построение Производного Класса
7.2.1 Построение Производного Класса Рассмотрим построение программы, которая имеет дело с людьми, служащими в некоторой фирме. Структура данных в этой программе может быть например такой:struct employee (* // служащий char* name; // имя short age; // возраст short department; // подразделение int salary; //
анализы: Конструирование трудностей "для мозгов"
анализы: Конструирование трудностей "для мозгов" Конструирование трудностей "для мозгов"Автор: Александр ПоддьяковОпубликовано в журнале "Компьютерра" N27-28 от 22 июля 2008 годаБорьба с трудностями имеет давнюю историю — продолжительностью в эволюцию жизни на Земле. И по
Объектно-ориентированное конструирование ПО
Объектно-ориентированное конструирование ПО У нас уже накоплено достаточно оснований, чтобы попытаться определить ОО-конструирование ПО. Это будет лишь первый набросок, более конкретное определение последует в следующей лекции.ОО-конструирование ПО (определение 1)
Конструирование объектно-ориентированного ПО
Конструирование объектно-ориентированного ПО Мы уже давали определение конструирования ОО-ПО: будучи весьма общим, оно представляет метод следующим образом: "основывать архитектуру всякой программной системы на модулях, полученных из типов объектов, с которыми