Подтвердить что ты не робот

Что может вызвать вызов чистой виртуальной функции в С++?

Я преподаю С++-класс программирования, и я видел достаточно классов ошибок, которые у меня есть хорошее чувство, как диагностировать распространенные ошибки С++. Однако существует одна серьезная ошибка, для которой моя интуиция не особенно хороша: какие ошибки программирования вызывают вызовы для чистых виртуальных функций? Наиболее распространенная ошибка, которую я видел, вызывает это вызов виртуальной функции из конструктора базового класса или деструктора. Есть ли другие, о которых я должен знать, когда помогает отлаживать код студента?

4b9b3361

Ответ 1

"Самая распространенная ошибка, которую я видел, которая вызывает это, вызывает виртуальную функцию от конструктора базового класса или деструктора".

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

Это может произойти, если вы следуете указателю на частично сконструированный объект (например, в состоянии гонки из-за асинхронных или поточных операций).

Если у компилятора есть основания полагать, что он знает реальный тип, к которому относятся точки указателя к базовому классу, он может разумно обойти виртуальную диспетчеризацию. Вы можете смутить его, выполнив что-то с поведением undefined как реинтерпрет.

Во время уничтожения виртуальная таблица рассылки должна быть возвращена, поскольку производные классы уничтожены, поэтому снова можно вызвать чистую виртуальную реализацию.

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

Ответ 2

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

  • Использование оборванного указателя - указатель не имеет действительного объекта, поэтому виртуальная таблица, на которую он указывает, представляет собой просто случайную память, которая может содержать NULL
  • Плохой прилив с использованием static_cast к неправильному типу (или приведение в стиле C) также может привести к тому, что объект, на который вы указываете, не имеет правильных методов в своей виртуальной таблице (в этом случае по крайней мере, это действительно виртуальная таблица в отличие от предыдущей опции).
  • DLL была выгружена. Если объект, который вы держите, был создан в общем объектном файле (DLL, so, sl), который был снова выгружен, память может быть обнулена

Ответ 3

Это может произойти, например, когда ссылка или указатель на объект указывает на местоположение NULL, и вы используете ссылку на объект или указатель на вызов виртуальной функции в классе. Например:

std::vector <DerivedClass> objContainer;  
if (!objContainer.empty()) 
   const BaseClass& objRef = objContainer.front();  
// Do some processing using objRef and then you erase the first
// element of objContainer
objContainer.erase(objContainer.begin());   
const std::string& name = objRef.name();  
// -> (name() is a pure virtual function in base class, 
// which has been implemented in DerivedClass).

В этот момент объект, хранящийся в objContainer [0], не существует. Когда виртуальная таблица индексируется, не обнаружено действительного местоположения памяти. Следовательно, выдается ошибка времени выполнения, говорящая "чистая виртуальная функция".