Стандарт C++ говорит, что вызов чистой виртуальной функции из конструктора или деструктора запрещен. Что является причиной этого? Почему в стандарте должно быть такое ограничение?
Чистый виртуальный вызов из конструктора и деструктора
Ответы (5)
В момент запуска деструктора класса все деструкторы подкласса уже были запущены. Было бы недопустимо вызывать виртуальный метод, определенный подклассом, для которого уже запущен его деструктор.
Аналогичное ограничение существует в отношении вызова виртуальных методов в конструкторах. Вы не можете вызвать виртуальный метод для подкласса, конструктор которого еще не запущен.
virtual function
, а не о pure virtual functions
- person Hicham; 28.12.2011
По той же причине нельзя жить в доме, пока заливаешь фундамент или разбираешь его. Пока конструктор не завершится, объект построен только частично. И как только деструктор запускается, объект частично уничтожается. Чистая виртуальная функция может быть вызвана только для объекта, который находится в нормальном состоянии, иначе структуры, необходимые для определения того, какую реализацию функции вызывать, могут не существовать.
В стандарте C++ сказано: «Вызов чистой виртуальной функции из конструктора или деструктора запрещен». Что является причиной этого? Почему в стандарте должно быть такое ограничение?
Из общеизвестно старого проекта стандарта С++, но соответствующие различия, которые я нарисую, остаются актуальными:
10.4-6 Функции-члены можно вызывать из конструктора (или деструктора) абстрактного класса; эффект виртуального вызова (class.virtual) чистой виртуальной функции прямо или косвенно для объекта, создаваемого (или уничтожаемого) из такого конструктора (или деструктора), не определен.
Это немного отличается от того, что вы утверждаете, поскольку контекст из фразы перед точкой с запятой неявно относится к пост-. Перефразируя:
undefined behaviour happens when an abstract class's constructor or destructor calls one of its own member functions that is (still) pure virtual.
Я включаю квалификатор "(до сих пор)", поскольку чистым виртуальным функциям даются определения - и они перестают быть "чистыми" - в какой-то точке иерархии классов. Это немного странно, но рассмотрите эту часть Стандарта:
Класс является абстрактным, если он имеет хотя бы одну чисто виртуальную функцию. [Примечание: такая функция может быть унаследована: см. ниже. ]
Ясно, что производные классы с определениями функции, которая была чисто виртуальной в базе, не обязательно сами по себе абстрактны. Приведенное выше утверждение может быть истинным только в том случае, если понятие «чистоты» применимо не ко всей виртуальной функции как таковой, а к уровням иерархии классов, на которых она остается чистой.
Следствие: если функция - с точки зрения уровня вызывающего конструктора/деструктора в иерархии - уже определена, ее можно вызвать с четко определенным поведением.
С этим пониманием того, что не определено в Стандарте, мы можем вернуться к вашим вопросам: «В чем причина этого? Почему Стандарт должен помещать такое ограничение?»
Суть в том, что базовый класс должен быть полностью сконструирован до того, как в дело вступит конструктор производного класса, поскольку конструкция производного класса может работать с базовым объектом. Точно так же базовый деструктор должен запускаться после производного деструктора, потому что последний может по-прежнему хотеть получить доступ к базовому объекту. Учитывая этот необходимый порядок, компилятор не может безопасно отправить виртуальную функцию производного класса, поскольку либо конструктор производного класса еще не запущен для установления состояния члена производного класса, либо деструктор производного класса уже вызвал деструкторы для дополнительных данных. члены, и снова состояние объекта больше не гарантируется пригодным для использования.
Для нечистых виртуальных функций компилятор может и действительно прибегает к вызову наиболее специализированного определения функции, известной для самого производного класса в иерархии, который уже был создан и еще не был уничтожен. Но чисто виртуальные функции по определению — это те, в которых еще не указана реализация, а на уровне иерархии классов, где функции являются чистыми, реализации для вызова не существует.
Типичная реализация механизма виртуальной диспетчеризации может представлять это наличием указателя в таблице виртуальной диспетчеризации для базового класса, содержащего чистую виртуальную функцию, которая установлена в 0, неинициализирована или указывает на какую-либо функцию "подать предупреждение". По мере запуска последовательных уровней конструкторов производных классов указатель на виртуальную диспетчерскую таблицу будет перезаписан адресом их собственных VDT. Те производные классы, которые переопределяют реализацию, будут указывать на собственное определение функции, которое станет значением по умолчанию для любых других производных классов, которые сами не определяют новую реализацию. Этот важнейший элемент неявного указателя на VDT будет перемещаться назад по тому же списку VDT, что и деструкторы производного класса, гарантируя, что любые виртуальные вызовы будут выполняться на неразрушенных уровнях в иерархии классов. Но после запуска деструктора первого класса, в котором была определена виртуальная функция, будущие VDT снова не будут ссылаться на реальную реализацию.
Напомним, что при вызове «нечистой» виртуальной функции из конструктора/деструктора игнорируется тот факт, что функция является виртуальной, и всегда вызывается реализация в вашем классе, а не в создаваемом производном классе. Вот почему вы не можете вызывать чисто виртуальную функцию из конструктора или деструктора: с их точки зрения, ваша чистая виртуальная функция не имеет реализации.
функция является лишь прототипом для реализации в подклассах, на самом деле она не существует в классе... поэтому ее нельзя вызвать ни в конструкторе, ни в деструкторе).
нет реализации функции, поэтому просто нет кода для вызова :)
подклассы, реализующие чистый виртуальный класс, не существуют при вызове конструктора/деструктора.