Страницы

Поиск по вопросам

суббота, 21 декабря 2019 г.

Возможные причины появления Debug Error - R6025 pure virtual function call

#cpp


Моя программа завершает своё выполнение с ошибкой:


  R6025 pure virtual function call. 


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


Ответы

Ответ 1



В ответе @AnT было объяснено, как может получиться такая ошибка. (Это вызов виртуальной функции, прямо или косвенно, в конструкторе или деструкторе.) Теперь вопрос в том, что делать. Для начала, попробуйте воспроизвести проблему под отладчиком. Microsoft советует заменить абстрактный метод на вызов DebugBreak, или запустить из-под отладчика и установить точки останова на _purecall в PureVirt.c. Но у меня проблема отловилась в Visual Studio 2015 и без этих заклинаний. Вы увидите в стеке, какой именно абстрактный метод вы вызываете, и поискав по стеку конструктор или деструктор объекта данного класса, найдёте ошибочный вызов. Помните, что это вполне может быть косвенный вызов, через другие функции. Если вам в конструкторе реально понадобился виртуальный метод, возможно, ваш конструктор делает слишком много. Может быть, имеет смысл вынести функциональность, требующую виртуальной функции, в отдельный метод, а конструктор закрыть, и конструировать класс через статическую фабричную функцию. Ещё одной причиной данной ошибки может служить вызов функции по указателю на уже умерший объект. Если деструктор объекта отработал, то при условии, что память, занимаемая объектом, никем не затёрта, при попытке вызова метода по мёртвому указателю будет также выполнен чисто виртуальный метод (эта ситуация аналогична вызову метода в деструкторе), с понятными последствиями. Так что если в вашем стеке нету конструктора/деструктора, всё куда хуже: у вас умер указатель.

Ответ 2



Если функция виртуальная, то используется позднее связывание, т.е. вызываться должна функция по типу объекта, Это верно, но не забываем, что пока работает конструктор или деструктор, текущий тип объекта (в т.ч. для целей виртуальных вызовов) - этот тот тип, чей конструктор/деструктор сейчас активен. Поэтому классический способ вызвать pure virtual функцию - это вызвать ее из конструктора через функцию-посредник class C { public: C() { foo(); } virtual void pure() = 0; void foo() { pure(); } }; class D : C { public: virtual void pure() {} }; int main() { D d; } Несмотря на то, что в конечном итоге мы создаем объект класса D, во время работы конструктора класса C никакого объекта класса D еще не существует. Все виртуальные вызовы во время работы конструктора класса C будут работать так, как будто мы имеем дело с объектом класса C. Формально для достижения того же эффекта вы можете попытаться вызвать pure прямо из C::C(), но компиляторы, как правило, оптимизируют такие виртуальные вызовы и заменяют их на невиртуальные. При этом они сразу замечают подвох еще на стадии компиляции. Обмануть компилятор в таком варианте можно, сделав вызов не напрямую, а через указатель на метод, что дает нам немножко более компактный способ достичь того же эффекта class C { public: C() { (this->*&C::pure)(); } virtual void pure() = 0; }; class D : C { public: virtual void pure() {} }; int main() { D d; }

Ответ 3



Моя проблема заключалась в том, что я обращался к "мёртвой ссылке", как было предсказано @VLadD. Вот упрощённая версия моего кода, с такой же ошибкой: #include using namespace std; class A { public: virtual void f()=0; virtual ~A(){} }; class B:public A { public: void f(){} }; class C:public A { A&a; public: C(A&a):a(a){} void f(){a.f();} }; class D { public: D(A&a){ a.f(); } }; int main() { C c( (B()) ); D d(c); system("pause"); } Если изменить метод main вот так: int main() { B b = B(); C c( b ); D d(c); system("pause"); } То ошибки не случится, ибо объект B() не будет удалён.

Комментариев нет:

Отправить комментарий