"순수한 가상 함수 호출"충돌은 어디에서 발생합니까?
가끔 내 컴퓨터에서 "순수 가상 함수 호출"오류와 함께 충돌하는 프로그램을 발견합니다.
추상 클래스로 객체를 만들 수 없을 때 이러한 프로그램은 어떻게 컴파일합니까?
생성자 또는 소멸자에서 가상 함수 호출을 시도하면 결과가 발생할 수 있습니다. 생성자 또는 소멸자 (파생 클래스 개체가 생성되지 않았거나 이미 소멸됨)에서 가상 함수 호출을 할 수 없기 때문에 순수 가상 함수의 경우 기본 클래스 버전을 호출합니다. 존재하지 않습니다.
( 여기에서 라이브 데모보기 )
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
}
순수 가상 함수가있는 개체의 생성자 또는 소멸자에서 가상 함수를 호출하는 표준 사례뿐만 아니라 개체가 소멸 된 후 가상 함수를 호출하면 최소한 MSVC에서 순수 가상 함수 호출을 얻을 수 있습니다. . 분명히 이것은 시도하고 수행하는 것은 매우 나쁜 일이지만 추상 클래스를 인터페이스로 사용하고 엉망이되면 볼 수 있습니다. 참조 카운트 된 인터페이스를 사용하고 있고 참조 카운트 버그가 있거나 다중 스레드 프로그램에서 객체 사용 / 객체 파괴 경쟁 조건이있는 경우 가능성이 더 높습니다. 이러한 종류의 purecall에 대한 점은 ctor 및 dtor에서 가상 통화의 '일반적인 용의자'를 확인하기 때문에 무슨 일이 일어나고 있는지 파악하기가 쉽지 않습니다.
이러한 종류의 문제를 디버깅하는 데 도움이되도록 다양한 버전의 MSVC에서 런타임 라이브러리의 purecall 처리기를 대체 할 수 있습니다. 이 시그니처를 사용하여 고유 한 기능을 제공하면됩니다.
int __cdecl _purecall(void)
런타임 라이브러리를 연결하기 전에 연결합니다. 이렇게하면 purecall이 감지 될 때 발생하는 작업을 제어 할 수 있습니다. 일단 당신이 제어권을 가지면 표준 핸들러보다 더 유용한 일을 할 수 있습니다. purecall이 발생한 위치에 대한 스택 추적을 제공 할 수있는 핸들러가 있습니다. 여기 참조 : http://www.lenholgate.com/blog/2006/01/purecall.html를 자세한 내용은.
(참고로 _set_purecall_handler ()를 호출하여 MSVC의 일부 버전에 핸들러를 설치할 수도 있습니다.)
일반적으로 매달린 포인터를 통해 가상 함수를 호출 할 때 인스턴스가 이미 파괴되었을 가능성이 높습니다.
더 많은 "창의적인"이유도있을 수 있습니다. 가상 기능이 구현 된 개체의 일부를 잘라낼 수 있었을 수 있습니다. 그러나 일반적으로 인스턴스가 이미 파괴되었습니다.
나는 파괴 된 객체로 인해 순수 가상 함수가 호출되는 시나리오를 만났고 Len Holgate
이미 아주 좋은 대답을 가지고 있습니다. 예를 들어 몇 가지 색상을 추가하고 싶습니다.
- 파생 객체가 생성되고 포인터 (Base 클래스)가 어딘가에 저장됩니다.
- Derived 개체가 삭제되었지만 포인터는 여전히 참조됩니다.
- 삭제 된 Derived 객체를 가리키는 포인터가 호출됩니다.
The Derived class destructor reset the vptr points to the Base class vtable, which has the pure virtual function, so when we call the virtual function, it actually calls into the pure virutal ones.
This could happen because of an obvious code bug, or a complicated scenario of race condition in multi-threading environments.
Here is an simple example (g++ compile with optimization turned off - a simple program could be easily optimized away):
#include <iostream>
using namespace std;
char pool[256];
struct Base
{
virtual void foo() = 0;
virtual ~Base(){};
};
struct Derived: public Base
{
virtual void foo() override { cout <<"Derived::foo()" << endl;}
};
int main()
{
auto* pd = new (pool) Derived();
Base* pb = pd;
pd->~Derived();
pb->foo();
}
And the stack trace looks like:
#0 0x00007ffff7499428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1 0x00007ffff749b02a in __GI_abort () at abort.c:89
#2 0x00007ffff7ad78f7 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3 0x00007ffff7adda46 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4 0x00007ffff7adda81 in std::terminate() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#5 0x00007ffff7ade84f in __cxa_pure_virtual () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#6 0x0000000000400f82 in main () at purev.C:22
Highlight:
if the object is fully deleted, meaning destructor gets called, and memroy gets reclaimed, we may simply get a Segmentation fault
as the memory has returned to the operating system, and the program just can't access it. So this "pure virtual function call" scenario usually happens when the object is allocated on the memory pool, while an object is deleted, the underlying memory is actually not reclaimed by OS, it is still there accessible by the process.
I'd guess there is a vtbl created for the abstract class for some internal reason (it might be needed for some sort of run time type info) and something goes wrong and a real object gets it. It's a bug. That alone should say that something that can't happen is.
Pure speculation
edit: looks like I'm wrong in the case in question. OTOH IIRC some languages do allow vtbl calls out of the constructor destructor.
I use VS2010 and whenever I try calling destructor directly from public method, I get a "pure virtual function call" error during runtime.
template <typename T>
class Foo {
public:
Foo<T>() {};
~Foo<T>() {};
public:
void SomeMethod1() { this->~Foo(); }; /* ERROR */
};
So I moved what's inside ~Foo() to separate private method, then it worked like a charm.
template <typename T>
class Foo {
public:
Foo<T>() {};
~Foo<T>() {};
public:
void _MethodThatDestructs() {};
void SomeMethod1() { this->_MethodThatDestructs(); }; /* OK */
};
If you use Borland/CodeGear/Embarcadero/Idera C++ Builder, your can just implement
extern "C" void _RTLENTRY _pure_error_()
{
//_ErrorExit("Pure virtual function called");
throw Exception("Pure virtual function called");
}
While debugging place a breakpoint in the code and see the callstack in the IDE, otherwise log the call stack in your exception handler (or that function) if you have the appropriate tools for that. I personally use MadExcept for that.
PS. The original function call is in [C++ Builder]\source\cpprtl\Source\misc\pureerr.cpp
Here is a sneaky way for it to happen. I had this essentially happen to me today.
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();
참고URL : https://stackoverflow.com/questions/99552/where-do-pure-virtual-function-call-crashes-come-from
'IT박스' 카테고리의 다른 글
모든 액티브 레코드가 싫어하는 이유는 무엇입니까? (0) | 2020.08.15 |
---|---|
Windows 배치 파일의 한 줄에 여러 명령 (0) | 2020.08.15 |
img 요소를 만드는 데 가장 좋은 JavaScript 코드는 무엇입니까 (0) | 2020.08.15 |
R의 ifelse 문이 벡터를 반환 할 수없는 이유는 무엇입니까? (0) | 2020.08.15 |
C #에서 튜플 (또는 배열)을 사전 키로 사용 (0) | 2020.08.15 |