Reputation: 5175
I'm getting a segmentation fault in my code while trying to calling destructor using a type-casted pointer . But If I change the destructor as a non-virtual one it is working properly .
#include <iostream>
using namespace std;
class Test
{
public:
Test() { cout << "Cons" << endl;}
~Test() {cout << "Des"<<endl;}
void *var_ptr;
};
class Test3
{
public:
Test3() { cout << "Cons3" << endl;}
//virtual ~Test3(){cout << "Des3" << endl;};
~Test3(){cout << "Des3" << endl;};
};
class Test2:public Test3
{
public:
Test2() { cout << "Cons2" << endl;}
~Test2() {cout << "Des2"<<endl;}
};
int main ()
{
Test *testPtr = new Test();
int *ivalue ;
ivalue = new int;
testPtr->var_ptr = (void*)ivalue;
((Test2*)(testPtr->var_ptr))->~Test2();
}
OutPut :
Cons
Segmentation fault
Without virtual dtor :
Output :
Cons
Des2
Des3
I can't avoid to use the type-casted pointer (Test2*) as far as my code . I want to understand why I'm getting a seg fault here while using virtual destructor . And is there any other way of type-casting the pointer to make it correct.
Upvotes: 1
Views: 2617
Reputation: 6329
When you add virtual member to a type its size actually increases - on x86 first four bytes contain address of vtable. You can witness it yourself using memory monitor. Anyways, program expects to find address of vtable (to get the address of top-level destructor function), but instead it finds the value of *ivalue.
By the way, I understand it is just an example, but you should make type names more meaningful.
EDIT: Well, this awakened my dark side and if you want to play extra dirty, here's the probably totally VS2010 specific code that appears to work (remember to uncomment virtual destructor in Test3):
int main()
{
// this is the actual signature of function that is called when you call destructor
// first parameter is the pointer to the object, second to the destructor itself
// (maybe simply first entry of the vtable?), and the last one is 0 if you
// call explicitly and 1 if called via delete
typedef void (__fastcall *destructor_func_t)(void*, void*, unsigned int);
// first acquire vtable address; you have to create dummy instance of your class
// to get it
Test2* dummy_instance = new Test2;
int* vtable = *reinterpret_cast<int**>(dummy_instance);
// destructor will be called here
delete dummy_instance;
int* someRubbish = new int;
// assume that destructor is the first entry in the table
destructor_func_t destructor = reinterpret_cast<destructor_func_t>(vtable[0]);
// destructor will be called here
destructor(someRubbish, destructor, 0);
delete someRubbish;
return 0;
}
Works on my machine, no exceptions thrown, output:
Cons3
Cons2
Des2
Des3
Des2
Des3
Upvotes: 3
Reputation: 208323
The problem is that your code triggers Undefined behavior, and that means that pretty much anything can happen. The program is invalid and crashing is one option, as not crashing is just another option of what a compliant compiler can do with your code. What you should do is correct your program.
Just to understand why the behavior, but not trying to imply that you can use this for anything: undefined behavior is undefined, and that means that you cannot depend on it ever. As of a practical explanation of what is happening, you can replicate a similar type of situation with the following code:
struct test1 {
void f() { printf( "Hi\n" ); }
};
struct test2 {
int x;
void f() { printf( "%d\n", x); }
};
int main() {
static_cast<test1*>(0)->f(); // probably won't crash
static_cast<test2*>(0)->f(); // probably will crash
}
This invalid program is conceptually similar to yours, with the difference that in this case the pointer does not refer to any valid memory, but is a null pointer. The practical explanation of the behavior has to do with how (most) compilers process the code. When you define a member function, the compiler generates the equivalent of a plain function where the first argument is a pointer to the object, and the rest of the arguments come afterwards. Whenever a member attribute is accessed, the pointer is dereferenced and the value at the other end is read, but if no members are used, then the compiler might not dereference the pointer at all. The code above will be compiled internally to something similar to the following C code:
struct test1 {};
void test1_f( struct test1* this ) {
printf( "Hi\n" );
}
struct test2 { int x; }
void test2_f( struct test2* this ) {
printf( "%d\n", p->x );
}
In the first case the pointer is not used at all, so even if it is a null pointer, because it is not dereferenced the code will seem to work (it is still invalid, and should be corrected, but in this particular implementation it will not crash). In the second case, the pointer is used, to access the member x
, and that will try to read memory around virtual address 0, which will trigger a segmentation fault and the program will crash.
Going back to the original problem, when you declare a virtual function, the compiler (it is not mandated, but all compilers do) will create a virtual table for each type in the hierarchy, and it will add a hidden pointer field to the object that refers to the virtual table of the particular type. The C++ code:
struct test {
virtual void f() { printf( "Hi\n" ); }
};
int main() {
static_cast<test*>(0)->f();
}
is translated into the equivalent of (for simplicity, assume that the compiler uses dynamic dispatch here, consider that the 0, is actually a call to a function that returns a NULL test*
so that the compiler, not knowing the type must use the dynamic dispatch):
struct test {
void (**__vptr)(); // hidden vptr
}
test_test(struct test* this) { // constructor
this->__vptr = &__test_vtable;
}
void test_f( struct test* this ) {
printf( "Hi\n" );
}
void (*__test_vtable)()[] = { &test_f }; // type is slightly off here
int main() {
((struct test*)(0)->__vptr)[ 0 ]( (struct test*)0 );
}
Where from top to bottom, the actual structure of the type is laid out, including the hidden pointer to the virtual table. A constructor is implicitly created and will set the value of the __vptr
to the appropriate table. In main
the pointer is first dereferenced to obtain the address of the final overrider and then the function at that address is called passing 0
as the this
pointer. Note that even if test_f
does not dereference the pointer, the caller has already tried to dereference to access the vtable
, and that triggers the segmentation fault and the crash.
Finally in your case, the virtual method is not just any method, but the destructor, that adds a bit of complexity to write the equivalent C code, as a matter of fact, depending on the ABI the compiler will generate multiple destructors, but the same problem shown in the previous case with the virtual f
function will trigger and the program will crash.
Upvotes: 5
Reputation: 3089
First, you did not declare the correct inherence, Test2 inherits from Test3 but you created a Test object. So, I supose that Test3 inherits from Test too:
class Test3: public Test
{
...
};
Second, if you want to destroy a Test2, you must create it:
Test *testPtr = new Test2();
Third, if you want to destroy a Test2 object via Test base class, you must declare virtual destructor in base class. So the compiler invoke, automatically both destructors:
class Test
{
virtual ~Test() { ... }
};
and you destroy Test2 allocated object simply
delete testPtr;
Fourth, check answer from @gwiazdorrr
Upvotes: 1