Чистый виртуальный вызов из конструктора и деструктора

Стандарт C++ говорит, что вызов чистой виртуальной функции из конструктора или деструктора запрещен. Что является причиной этого? Почему в стандарте должно быть такое ограничение?


person nitin_cherian    schedule 28.12.2011    source источник
comment
Там не сказано "запрещено". Говорит не определено. Причина в том, чтобы предоставить компиляторам максимальную гибкость.   -  person Martin York    schedule 28.12.2011


Ответы (5)


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

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

person Greg Hewgill    schedule 28.12.2011
comment
здесь вы говорите о virtual function, а не о pure virtual functions - person Hicham; 28.12.2011
comment
Мои комментарии о виртуальных функциях применимы и к чисто виртуальным функциям. - person Greg Hewgill; 28.12.2011
comment
но чистая виртуальная функция более тривиальна: ее вообще нельзя вызывать в классе (конструктор, деструктор или другой метод). - person Hicham; 28.12.2011
comment
@eharvest: это неправда, чистая виртуальная функция может быть вызвана из любого метода, не являющегося конструктором/деструктором. Подклассы должны обеспечивать реализацию. - person Greg Hewgill; 28.12.2011
comment
чистая виртуальная функция может быть вызвана из любого метода, не являющегося конструктором/деструктором; из базового класса? ... :/ - person Hicham; 28.12.2011
comment
это не соответствует принципам OP: f () чистый виртуальный в базовом классе, я называю его в g () другой функцией в базовом классе: если существует более одного подкласса, какую реализацию следует вызывать? не сенс. мне нужно указать реализацию подкласса для использования B::f() : поэтому базовый класс должен знать подклассы oits: бессмысленно с OP. - person Hicham; 28.12.2011
comment
@eharvest: когда виртуальная функция вызывается из базового класса, фактическая вызываемая функция зависит от типа экземпляра, для которого вызывается функция. Это не зависит от количества подклассов. - person Greg Hewgill; 28.12.2011
comment
@ Грег Хьюгилл: хорошо, я нашел этот хитрый пример: codeproject.com/KB/cpp /PureVirtualFunctionCall.aspx - person Hicham; 28.12.2011
comment
в деструкторе, вызывая функцию, вызывающую чистый виртуальный. используемый компилятор не обнаруживает ошибку - person Hicham; 28.12.2011
comment
В C++ можно делать множество неопределенных вещей, которые компилятор не может обнаружить. - person Greg Hewgill; 28.12.2011
comment
Вы можете вызывать виртуальные методы как из конструктора, так и из деструктора, и это имеет четко определенную семантику в C++: «вызываемая функция является окончательным переопределением в классе конструктора или деструктора, а не переопределением в более производном классе». И именно по этой причине вызов чисто виртуальных функций приводит к неопределенному поведению. - person Konstantin Oznobihin; 28.12.2011
comment
окончательный переопределение, что это значит? Вы имеете в виду последний созданный подкласс, переопределяющий виртуальную функцию? - person Hicham; 28.12.2011
comment
@eharvest: мне кажется, что для этого вам следует задать полноценный вопрос, к сожалению, комментарии предлагают ограниченную поддержку структурированных ответов. - person Matthieu M.; 28.12.2011
comment
@MatthieuM.: см. 12.7/4 в C++11 или 12.7/3 в C++2003, формулировка несколько отличается, но смысл тот же: вы можете вызывать виртуальные функции из конструкторов и деструкторов. Это не какое-то нестандартное расширение. - person Konstantin Oznobihin; 28.12.2011
comment
@KonstantinOznobihin: ты прав, извини. Я как-то имел в виду, что это результат конкретной детали реализации. - person Matthieu M.; 28.12.2011
comment
если он определен в классе или в родительском классе - person Hicham; 28.12.2011

По той же причине нельзя жить в доме, пока заливаешь фундамент или разбираешь его. Пока конструктор не завершится, объект построен только частично. И как только деструктор запускается, объект частично уничтожается. Чистая виртуальная функция может быть вызвана только для объекта, который находится в нормальном состоянии, иначе структуры, необходимые для определения того, какую реализацию функции вызывать, могут не существовать.

person David Schwartz    schedule 28.12.2011
comment
И это отличается от не чистой виртуальной функции... чем? - person Lightness Races in Orbit; 08.02.2012

В стандарте 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 снова не будут ссылаться на реальную реализацию.

person Tony Delroy    schedule 28.12.2011

Напомним, что при вызове «нечистой» виртуальной функции из конструктора/деструктора игнорируется тот факт, что функция является виртуальной, и всегда вызывается реализация в вашем классе, а не в создаваемом производном классе. Вот почему вы не можете вызывать чисто виртуальную функцию из конструктора или деструктора: с их точки зрения, ваша чистая виртуальная функция не имеет реализации.

person Sergey Kalinichenko    schedule 28.12.2011

функция является лишь прототипом для реализации в подклассах, на самом деле она не существует в классе... поэтому ее нельзя вызвать ни в конструкторе, ни в деструкторе).

нет реализации функции, поэтому просто нет кода для вызова :)

подклассы, реализующие чистый виртуальный класс, не существуют при вызове конструктора/деструктора.

person Hicham    schedule 28.12.2011