Skąd się biorą awarie "pure virtual function call"?

Czasami zauważam programy, które zawieszają się na moim komputerze z błędem: "pure virtual function call".

Jak te programy kompilują się, gdy obiekt nie może być utworzony z klasy abstrakcyjnej?

Author: Wolf, 2008-09-19

7 answers

Mogą wynikać, jeśli spróbujesz wywołać wirtualną funkcję z konstruktora lub destruktora. Ponieważ nie można wywołać funkcji wirtualnej z konstruktora lub destruktora (obiekt klasy pochodnej nie został skonstruowany lub został już zniszczony), wywołuje ona wersję klasy bazowej, która w przypadku czystej funkcji wirtualnej nie istnieje.

(Zobacz live demo tutaj )

class Base
{
public:
    Base() { doIt(); }  // DON'T DO THIS
    virtual void doIt() = 0;
};

void Base::doIt()
{
    std::cout<<"Is it fine to call pure virtual function from constructor?";
}

class Derived : public Base
{
    void doIt() {}
};

int main(void)
{
    Derived d;  // This will cause "pure virtual function call" error
}
 98
Author: Adam Rosenfield,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-16 06:30:48

Jak również standardowy przypadek wywołania funkcji wirtualnej z konstruktora lub destruktora obiektu z czystymi funkcjami wirtualnymi, można również uzyskać czyste wywołanie funkcji wirtualnej (przynajmniej na MSVC), jeśli wywołasz funkcję wirtualną po zniszczeniu obiektu. Oczywiście jest to dość zła rzecz, ale jeśli pracujesz z klasami abstrakcyjnymi jako interfejsami i nawalisz, to jest coś, co możesz zobaczyć. Jest to prawdopodobnie bardziej prawdopodobne, jeśli używasz liczone interfejsy i masz błąd zliczania refów lub jeśli masz object use/object destruction race condition w programie wielowątkowym... Rzecz w tego rodzaju purecall jest to, że często mniej łatwo jest pojąć, co się dzieje, jako czek dla "zwykłych podejrzanych" wirtualnych połączeń w ctor i dtor wyjdzie czysty.

Aby pomóc w debugowaniu tego typu problemów, można w różnych wersjach MSVC zastąpić funkcję obsługi purecall biblioteki uruchomieniowej. Robisz to przez podanie własnej funkcji z tym podpisem:

int __cdecl _purecall(void)

I linkowanie go przed połączeniem biblioteki runtime. Daje to kontrolę nad tym, co dzieje się po wykryciu purecall. Gdy już masz kontrolę, możesz zrobić coś bardziej użytecznego niż standardowy program obsługi. Mam handler, który może dostarczyć ślad stosu, gdzie doszło do purecall; zobacz tutaj: http://www.lenholgate.com/blog/2006/01/purecall.html Po Więcej Szczegółów.

(Uwaga Możesz również wywołać _set_purecall_handler() aby zainstalować program obsługi w niektórych wersjach MSVC).

 61
Author: Len Holgate,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-24 06:19:38

Zazwyczaj, gdy wywołujesz funkcję wirtualną przez zwisający wskaźnik - najprawdopodobniej instancja została już zniszczona.

Mogą być też bardziej "kreatywne" powody: być może udało ci się odciąć część obiektu, w której zaimplementowano funkcję wirtualną. Ale zazwyczaj chodzi o to, że instancja została już zniszczona.

 7
Author: Braden,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-19 04:15:51

Domyślam się, że istnieje vtbl stworzony dla klasy abstract z jakiegoś wewnętrznego powodu (może być potrzebny do jakiegoś rodzaju informacji o czasie wykonywania) i coś pójdzie nie tak i prawdziwy obiekt to dostanie. To pluskwa. To samo powinno powiedzieć, że coś, co nie może się zdarzyć, jest.

Czysta spekulacja

Edit: wygląda na to, że się mylę w omawianej sprawie. OTOH IIRC niektóre języki zezwalają na wywołania vtbl z konstruktora destructor.

 0
Author: BCS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-19 04:24:26

Używam VS2010 i za każdym razem, gdy próbuję wywołać destructor bezpośrednio z publicznej metody, dostaję błąd "pure virtual function call" podczas wykonywania.

template <typename T>
class Foo {
public:
  Foo<T>() {};
  ~Foo<T>() {};

public:
  void SomeMethod1() { this->~Foo(); }; /* ERROR */
};

Więc przeniosłem to, co jest wewnątrz ~ Foo (), aby oddzielić metodę prywatną, a potem zadziałało jak urok.

template <typename T>
class Foo {
public:
  Foo<T>() {};
  ~Foo<T>() {};

public:
  void _MethodThatDestructs() {};
  void SomeMethod1() { this->_MethodThatDestructs(); }; /* OK */
};
 0
Author: David Lee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-16 05:08:46

Jeśli używasz Borland / CodeGear / Embarcadero / Idera C++ Builder, możesz po prostu zaimplementować

extern "C" void _RTLENTRY _pure_error_()
{
    //_ErrorExit("Pure virtual function called");
    throw Exception("Pure virtual function called");
}

Podczas debugowania umieść punkt przerwania w kodzie i zobacz stack wywołań w IDE, w przeciwnym razie Zaloguj stos wywołań w obsłudze wyjątków (lub tej funkcji), jeśli masz odpowiednie do tego narzędzia. Osobiście używam do tego MadExcept.

PS. Oryginalne wywołanie funkcji znajduje się w [C++ Builder] \ source \ cpprtl \ Source \ misc \ pureerr.cpp

 0
Author: Niki,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-22 14:52:17

Oto podstępny sposób, aby to się stało. To mi się dzisiaj przytrafiło.

class A
{
  A *pThis;
  public:
  A()
   : pThis(this)
  {
  }

  void callFoo()
  {
    pThis->foo(); // call through the pThis ptr which was initialized in the constructor
  }

  virtual void foo() = 0;
};

class B : public A
{
public:
  virtual void foo()
  {
  }
};

B b();
b.callFoo();
 -2
Author: 1800 INFORMATION,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-19 04:22:58