Программирование на Visual C++. Архив рассылки
Шрифт:
Из конструктора даже виртуальные методы вызываются в соостветствии со статическим (а не динамическим) типом класса. Т.е. при вызове метода 'foo' из конструктора класса 'A' всегда вызывается метод 'A::foo', независимо от того, переопределялся ли это метод в наследнике или нет. В этом есть смысл: в момент выполнения конструктора базового класса класс-наследник еще не сконструировался и попытки вызывать его методы ни к чему хорошему не приведут (см. пример от IT)
Всё правильно, но я бы хотел уточнить некоторые детали реализации. На самом деле функции вызываются как обычно, т.е. по всем правилам вызова виртуальных функций через VMT. Если бы даже конструктор и вызывал виртуальные функции по другому, то его можно было бы легко обмануть, вызвав из него любую функцию из которой уже вызвать виртуальную.
Всё гораздо проще. Указатель на VMT инициализируется, как известно, в самом
Все это действительно детали реализации. В данном конкретном случае естественный порядок инициализации указателя на VMT как нельзя лучше способствует соблюдению спецификации языка. Создатели компиляторов знают об этой спецификации и, например, в MSVC++ виртуальные методы при прямом вызове их из конструктора вызываются статически, а не виртуально (т.е. в общем случае ты не прав, говоря, что "на самом деле функции вызываются как обычно"). А вот если попытаться сделать непрямой вызов виртуального метода (через метод-последник), то тут уже действительно срабатывает "правильное" значение указателя на VMT.
U>> Кто-нибудь подскажет, как решить данную проблему?
Разделить создание объекта и конструирование, например, ввести метод Init, вызываемый после конструктора.
В принципе, можно поступить поизящней – уложить подобные действия в шаблонную функцию, как например в ATL реализована CComObject::CreateInstance.
АТ>Из конструктора даже виртуальные методы вызываются в соостветствии со статическим (а не динамическим) типом класса. Т.е. при вызове метода 'foo' из конструктора класса 'A' всегда вызывается метод 'A::foo', независимо от того, переопределялся ли это метод в наследнике или нет. В этом есть смысл: в момент выполнения конструктора базового класса класс-наследник еще не сконструировался и попытки вызывать его методы ни к чему хорошему не приведут (см. пример от IT)
А кроме того – можно попасться на вызове чисто виртуального метода, когда указатель на него еще не установлен. Например – так:
При создании объекта класса D первым, как полагается, будет создан объект
Это все на сегодня. До встречи!
Программирование на Visual C++
Выпуск №54 от 11 ноября 2001 г.
Здравствуйте, уважаемые подписчики!
Я наконец разобрался с CHM-файлом архива рассылки, так что теперь там поиск должен работать нормально (правда, как и раньше, только для латиницы). Новый файл, в который я заодно добавил вышедшие за это время выпуски, можно скачать здесь.
Сегодня в выпуске – долгожданное продолжение руководства по WTL Александра Шаргина. К сожалению из-за большого объема статьи, которую даже пришлось разбить на две части, остальных рубрик сегодня не будет. Но статья того без сомнения стоит!
CТАТЬЯ
Использование WTL
Часть 2. Диалоги и контролы
Автор: Александр Шаргин
Диалоговые окна широко используются в Windows-приложениях, начиная с момента выхода самой операционной системы Windows. Они очень удобны для организации диалога с пользователем (отсюда их название). Кроме того, в несложных приложениях часто удаётся построить на базе диалогов не только вспомогательные окна, но и главное окно приложения (такие приложения иногда называют "dialog-based"). В этом разделе мы рассмотрим классы WTL, предназначенные для работы с диалоговыми окнами.
Классы WTL, относящиеся к диалоговым окнам, показаны на рисунке 1.
Рисунок 1. Диалоговые классы WTL
Обратите внимание, что все диалоговые классы порождаются от базового класса CWindowImplRoot<>, а не от класса CWindowImpl<>. Это сделано потому, что диалоги, в отличие от всех остальных окон, не используют оконную процедуру для обработки сообщений. Вместо этого используется диалоговая процедура, адрес которой задаётся при создании диалога. WTL предоставляет вам свою реализацию диалоговой процедуры в классе CDialogImplBaseT<>. Соответственно, все остальные классы диалогов WTL наследуют эту реализацию.
ПРИМЕЧАНИЕ
Все классы, показанные на рисунке 1, WTL унаследовала от библиотеки ATL. Они описаны в файле atlwin.h
Теперь изучим каждый класс более подробно.
Итак, класс CDialogImplBaseT<> содержит функциональность, необходимую всем без исключения диалоговым окнам. Это, в первую очередь, поддержка диалоговых процедур, а также пара вспомогательных функций. Обратите внимание, что в класс CDialogImplBaseT<> не встроен механизм создания диалога при помощи функций DialogBox и CreateDialog. Дело в том, что не все диалоги нуждаются в этих функциях. Например, стандартные диалоги создаются при помощи специальных функций (GetOpenFileName, ChooseColor и т. д.).
Диалоговые процедуры в классе CDialogImplBaseT<> реализованы более или менее аналогично оконным процедурам в классе CWindowImplBaseT<>.