Что может вызвать чисто виртуальный вызов функции в C ++? - PullRequest
33 голосов
/ 06 января 2011

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

Ответы [ 3 ]

28 голосов
/ 06 января 2011

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

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

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

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

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

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

8 голосов
/ 06 января 2011

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

  1. Использование висячего указателя - указатель не является допустимым объектом, поэтому виртуальная таблицаон указывает на случайную память, которая может содержать NULL
  2. Неверное приведение с использованием static_cast к неверному типу (или приведение в стиле C) также может вызвать объект, на который вы указываетене имеют правильных методов в своей виртуальной таблице (в данном случае, по крайней мере, является виртуальной таблицей в отличие от предыдущего варианта).
  3. DLL была выгружена -Если объект, за который вы держитесь, был создан в общем объектном файле (DLL, итак, sl), который был выгружен снова, память теперь можно обнулить
0 голосов
/ 22 июля 2011

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

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], не существует.Когда виртуальная таблица проиндексирована, правильная ячейка памяти не найдена.Следовательно, во время выполнения выдается сообщение «чисто виртуальная функция вызвана».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...