18.5.2. Специальная семантика инициализации
18.5.2. Специальная семантика инициализации
Наследование, в котором присутствует один или несколько виртуальных базовых классов, требует специальной семантики инициализации. Взгляните еще раз на реализации Bear и Raccoon в предыдущем разделе. Видите ли вы, какая проблема связана с порождением класса Panda?
class Panda : public Bear,
public Raccoon, public Endangered {
public:
Panda( string name, bool onExhibit=true );
virtual ostream& print( ostream& ) const;
bool sleeping() const { return _sleeping; }
void sleeping( bool newval ) { _sleeping = newval; }
// ...
protected:
bool _sleeping;
// ...
};
Проблема в том, что конструкторы базовых классов Bear и Raccoon вызывают конструктор ZooAnimal с неявным набором аргументов. Хуже того, в нашем примере значения по умолчанию для аргумента fam_name (название семейства) не только отличаются, они еще и неверны для Panda.
В случае невиртуального наследования производный класс способен явно инициализировать только свои непосредственные базовые классы (см. раздел 17.4). Так, классу Panda, наследующему от ZooAnimal, не разрешается напрямую вызвать конструктор ZooAnimal в своем списке инициализации членов. Однако при виртуальном наследовании только Panda может напрямую вызывать конструктор своего виртуального базового класса ZooAnimal.
Ответственность за инициализацию виртуального базового возлагается на ближайший производный класс. Например, когда объявляется объект класса Bear:
Bear winnie( "pooh" );
то Bear является ближайшим производным классом для объекта winnie, поэтому выполняется вызов конструктора ZooAnimal, определенный в классе Bear. Когда мы пишем:
cout winnie.family_name();
будет выведена строка:
The family name for pooh is Bear
(Название семейства для pooh – это Bear)
Аналогично для объявления
Raccoon meeko( "meeko");
Raccoon – это ближайший производный класс для объекта meeko, поэтому выполняется вызов конструктора ZooAnimal, определенный в классе Raccoon. Когда мы пишем:
cout meeko.family_name();
печатается строка:
The family name for meeko is Raccoon
(Название семейства для meeko - это Raccoon)
Если же объявить объект типа Panda:
Panda yolo( "yolo" );
то ближайшим производным классом для объекта yolo будет Panda, поэтому он и отвечает за инициализацию ZooAnimal.
Когда инициализируется объект Panda, то явные вызовы конструктора ZooAnimal в конструкторах классов Raccoon и Bear не выполняются, а вызывается он с теми аргументами, которые указаны в списке инициализации членов объекта Panda. Вот так выглядит реализация:
Panda::Panda( string name, bool onExhibit=true )
: ZooAnimal( name, onExhibit, "Panda" ),
Bear( name, onExhibit ),
Raccoon( name, onExhibit ),
Endangered( Endangered::environment,
Endangered::critical ),
sleeping( false )
{}
Если в конструкторе Panda аргументы для конструктора ZooAnimal не указаны явно, то вызывается конструктор ZooAnimal по умолчанию либо, если такового нет, выдается ошибка при компиляции определения конструктора Panda.
Когда мы пишем:
cout yolo.family_name();
печатается строка:
The family name for yolo is Panda
(Название семейства для yolo - это Panda)
Внутри определения Panda классы Raccoon и Bear являются промежуточными, а не ближайшими производными. В промежуточном производном классе все прямые вызовы конструкторов виртуальных базовых классов автоматически подавляются. Если бы от Panda был в дальнейшем произведен еще один класс, то сам класс Panda стал бы промежуточным и вызов из него конструктора ZooAnimal также был бы подавлен.
Обратите внимание, что оба аргумента, передаваемые конструкторам Bear и Raccoon, излишни в том случае, когда они выступают в роли промежуточных производных классов. Чтобы избежать передачи ненужных аргументов, мы можем предоставить явный конструктор, вызываемый, когда класс оказывается промежуточным производным. Изменим наш конструктор Bear:
class Bear : public virtual ZooAnimal {
public:
// если выступает в роли ближайшего производного класса
Bear( string name, bool onExhibit=true )
: ZooAnimal( name, onExhibit, "Bear" ),
_dance( two_left_feet )
{}
// ... остальное без изменения
protected:
// если выступает в роли промежуточного производного класса
Bear() : _dance( two_left_feet ) {}
// ... остальное без изменения
};
Мы сделали этот конструктор защищенным, поскольку он вызывается только из производных классов. Если аналогичный конструктор по умолчанию обеспечен и для класса Raccoon, можно следующим образом модифицировать конструктор Panda:
Panda::Panda( string name, bool onExhibit=true )
: ZooAnimal( name, onExhibit, "Panda"),
Endangered( Endangered::environment,
Endangered::critical ),
sleeping( false )
{}
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
5. Семантика
5. Семантика HTML не дает нам огромного количества элементов для работы. Тот ассортимент, что у нас есть, скорее похож на ассортимент магазинчика на углу, а не гипермаркета.У нас есть абзацы, списки и заголовки, но нет событий, репортажей и рецептов. HTML дает нам элемент,
Специальная панель инструментов
Специальная панель инструментов Специальная панель инструментов располагается между окном браузера и окном диаграммы в средней части рабочего интерфейса. По умолчанию предлагается панель инструментов для построения диаграммы классов модели (рис. 12.5). Рис. 12.5. Внешний
Семантика вызова
Семантика вызова Вызов локальной процедуры однозначно приводит к ее выполнению, после чего управление возвращается в головную программу. Иначе дело обстоит при вызове удаленной процедуры. Невозможно установить, когда конкретно будет выполняться процедура, будет ли она
1.4.6. Специальная пиктограмма для каталога
1.4.6. Специальная пиктограмма для каталога Щелкните правой кнопкой мыши на каталоге, выберите в контекстном команду Свойства и в открывшемся окне свойств перейдите во вкладку Эмблема (рис. 1.57). Здесь вы можете выбрать любой значок для нашего каталога. Выбранный вами значок
Семантика параметров
Семантика параметров До этого мы имели дело с синтаксисом передачи параметров и получили механизм синтаксического анализа для его обработки. Сейчас мы должны рассмотреть семантику, т.е. действия, которые должны быть предприняты когда мы столкнемся с параметрами. Это
16.6. Семантика вызовов
16.6. Семантика вызовов В листинге 15.24 мы привели пример клиента интерфейса дверей, повторно отсылавшего запрос на сервер при прерывании вызова door_call перехватываемым сигналом. Затем мы показали, что при этом процедура сервера вызывается дважды, а не однократно. Потом мы
4.1. Синтаксис и семантика
4.1. Синтаксис и семантика Прежде чем двигаться дальше, введем базовые определения. Языком мы будем называть множество строк (в большинстве случаев это будет бесконечное множество). Каждое выражение (в некоторых источниках вместо "выражение" употребляются термины
9.3. Создание безопасного при исключениях списка инициализации
9.3. Создание безопасного при исключениях списка инициализации ПроблемаНеобходимо инициализировать ваши данные-члены в списке инициализации конструктора, и поэтому вы не можете воспользоваться подходом, описанным в рецепте 9.2.РешениеИспользуйте специальный формат
Синтаксис инициализации членов-переменных
Синтаксис инициализации членов-переменных Типы класса обычно имеют множество членов-переменных (также называемых полями). Если в классе можно определять множество конструкторов, то может возникнуть не слишком радующая программиста необходимость многократной записи
14.5. Список инициализации членов
14.5. Список инициализации членов Модифицируем наш класс Account, объявив член _name типа string:#include stringclass Account {public:// ...private:unsigned int _acct_nmbr;double _balance;string _name;};Придется заодно изменить и конструкторы. Возникает две проблемы: поддержание совместимости с первоначальным
Специальная тема миграции: диалекты SQL
Специальная тема миграции: диалекты SQL Если вы бывшие пользователи InterBase или вы использовали устаревшие инструменты миграции для конвертирования других СУБД в InterBase, то диалекты SQL видимо будут влиять на некоторые аспекты новой жизни ваших баз данных и приложений при
Специальная тема: настройка безопасности пользователя
Специальная тема: настройка безопасности пользователя Идентификация пользователя Firebird довольно проблематична. Хорошая новость: все изменится к лучшему в Firebird 2. Плохая новость: нам придется с этим жить еще некоторое время. Ivan Prenosil- эксперт, в течение длительного времени
3.2. Процедура инициализации
3.2. Процедура инициализации Каждый драйвер имеет процедуру инициализации . Эта процедура вызывается системой сразу после загрузки драйвера в память.У нас такая процедура называется DriverEntry. Объявим её какDriver Entry proc near public, DriverObject:PDRIVER_OBJECT, RegistryPath:PUNICODE_STRINGDriverObject – это
Перекрытие инициализации по умолчанию
Перекрытие инициализации по умолчанию Для использования инициализации, отличной от предопределенной умолчанием, необходимо класс снабдить одной или несколькими процедурами создания. Такие процедуры должны быть перечислены в предложении, начинающимся ключевым словом