2016-08-01 4 views
9

У меня есть немного борьбы с Microsoft Visual C++ 2015 и я смог воспроизвести проблему с помощью небольшой программы. Учитывая следующие классы:C++: необъяснимая ошибка «чистой виртуальной функции»

class BaseClass { 
public: 
    BaseClass() 
     : mValue(0) 
     , mDirty(true) 
    {} 
    virtual ~BaseClass() {} 
    virtual int getValue() const { if(mDirty) updateValue(); return mValue; } 

protected: 
    virtual void updateValue() const = 0; 

    mutable bool mDirty; 
    mutable int mValue; 
}; 

class DerivedClass : public BaseClass { 
public: 
    DerivedClass() {} 

protected: 
    void updateValue() const override 
    { 
     mValue++; 
     mDirty = false; 
    } 
}; 

class Impersonator { 
public: 
    Impersonator() {} 

    // conversion operator 
    operator DerivedClass() const 
    { 
     return DerivedClass(); 
    } 

    // conversion method 
    DerivedClass toDerived() const 
    { 
     return DerivedClass(); 
    } 
}; 

Я получаю «чистый вызов виртуальной функции» ошибка, когда я делаю следующее:

void use(const BaseClass &inst) 
{ 
    // calls `getValue` which in turns calls the virtual function 'updateValue' 
    int value = inst.getValue(); 
} 

int main() 
{ 
    // creates a temporary, then passes it by reference: 
    use(DerivedClass()); // this works 

    // calls conversion operator to create object on stack, then passes it by reference: 
    DerivedClass i = Impersonator(); 
    use(i); // this works 

    // calls conversion method to create a temporary, then passes it by reference: 
    use(Impersonator().toDerived()); // this works 

    // calls conversion operator to create a temporary, then passes it by reference: 
    Impersonator j = Impersonator(); 
    use(j); // causes a pure virtual function call error! 

    return 0; 
} 

Учитывая, что я не могу изменить функцию void use(const BaseClass&), я могу изменить что-нибудь в классе Impersonator, чтобы разрешить использование последнего вызова без генерации ошибки отладки?

+0

Являются ли ваши компоненты времени выполнения в Visual Studio в порядке? Возможно, некоторые недостающие компоненты вызывают эту проблему. – pilkington

+1

Если вы оставите точку в последнем вызове 'getValue' и проверите указатель vtable, MSVC считает, что у вас есть объект BaseClass, который выглядит некорректно. – Praetorian

+0

http://stackoverflow.com/questions/99552/where-do-pure-virtual-function-call-crashes-come- from –

ответ

2

Единственный способ смягчить проблему, которую я вижу, - добавить operator const BaseClass&() в Impersonator и вернуть ссылку на DerivedClass.

Это создаст лучшее преобразование, чем проблема/ошибка, которую пытается использовать компилятор.

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

class Impersonator { 
public: 
    Impersonator() {} 

    // conversion operator 
    operator DerivedClass() 
    { 
     return d; 
    } 
    operator const BaseClass&() 
    { 
     return d; 
    } 

private: 
    DerivedClass d; 
}; 
+0

Да, это работает. К сожалению, «имитатор» в самом приложении - это класс LightSource, пытающийся выдавать себя за изображение, и его члены данных должны строго следовать правилам макета std140, поэтому я не могу добавить элемент данных DerivedClass. Я мог бы создать статический код в операторе преобразования, но тогда только один такой «временный» может быть активным, и он также не является потокобезопасным. Спасибо за ваш вклад. –

+0

Частичное решение: если я использую метод 'Impersonator' для создания' DerivedClass', вместо оператора преобразования он работает: 'DerivedClass toDerived() const {return DerivedClass(); } '. Я добавлю это к образцу кода. –

+0

Вы можете выделить объекты DerivedClass динамически и поместить их в локальный поток. Очистите его, когда вы не знаете, какие объекты DerivedClass могут понадобиться. Или, если вы можете обернуть 'use', удалите динамический объект в обертке. –

1

Это обходное решение. Создайте обертку для use, которая принимает const DerivedClass&.

//I get a "pure virtual function call" error when I do the following : 
void use(const BaseClass &inst) 
{ 
    // calls `getValue` which in turns calls the virtual function 'updateValue' 
    int value = inst.getValue(); 
} 

void use(const DerivedClass &inst) { 
    use(static_cast<const BaseClass&>(inst)); 
} 

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

Смежные вопросы