[[ blog 이사 과정에서 정확한 posting날짜가 분실됨. 년도와 분기 정도는 맞지 않을까? ]]
At first, see [ virtual method table in wikipedia ].
'pointer fixups' is needed to support inheritance in C++.
Let's dig deeper.. See following example. (classes comes from above Wikipedia).
// <CASE 1>
#ifdef _CASE_1_
class B1
{
public:
void f0() {}
virtual void f1() {}
int int_in_b1;
};
class B2
{
public:
virtual void f2() {}
int int_in_b2;
};
class D : public B1, public B2
{
public:
void d() {}
void f2() {} // override B2::f2()
int int_in_d;
};
#endif // _CASE_1_
// <CASE 2>
#ifdef _CASE_2_
class B1
{
public:
virtual ~B1(){}
void f0() {}
virtual void f1() {}
int int_in_b1;
};
class B2
{
public:
virtual ~B2(){}
virtual void f2() {}
int int_in_b2;
};
class D : public B1, public B2
{
public:
~D(){}
void d() {}
void f2() {} // override B2::f2()
int int_in_d;
};
#endif // _CASE_2_
void funcA(B1* obj) {
// -------- (*1)
}
void funcB(B2* obj) {
// -------- (*2)
}
void
main()
{
D* p = new D;
funcA(dynamic_cast<B1*>(p)); // -------- (*a)
funcB(dynamic_cast<B2*>(p)); // -------- (*b)
// delete p; // ----- (*l)
// delete (dynamic_cast<B1*>(p)); // ----- (*m)
// delete (dynamic_cast<B2*>(p)); // ----- (*n)
}
We can see that pointer 'p' is passed as parameter in (*a) and (*b). And to do this type-casting and 'pointer fixup' is needed. That is, actual memory address passed as parameter of (*a) is different from the one of (*b). This is 'pointer fixup'. In case of GCC and MSVC7, there is 8-byte-gap as above wikipedia said.; { Address_of_obj_at_(*1) + 8 = Address_of_obj_at_(*2) }.
That's OK. It's very common.
Then, what happen at the moment of deleting memory?
We may guess that memory address passed is same in (*l) and (*m), but not in (*n) - may be 8-byte-gap in GCC and MSVC7 same as the case of (*a) and (*b). In <CASE_1>, program works just as we expects. (*l) and (*m) don't make an exception at runtime, but (*n) does. Then, as you know, why <CASE_2> works well? Newly assigned memory is deleted correctly in <CASE_2>. Than, is it really true that passed addresses are different among (*l), (*m) and (*n)? To investigate deeper, I tested it again with overloaded new/delete operator.
void * operator new(size_t size) {
return malloc(size); // ------ (*A)
}
void operator delete(void *p) {
return free(p); // ----- (*B)
}
We can check passed address at (*B). In <CASE_1>, 'p' at (*B) is same with 'obj' at (*2), and error is raised - memory address trying to free is different with allocated one. Difference between <CASE_1> and <CASE_2> is destructor declared as 'virtual'. Interestingly, in <CASE_2>, 'p' at (*B) is the value of 'obj' at (*1) - original address -, and work successfully. In summary, if 'virtual destructor' is defined, than original address value is passed to 'delete' operator. It is interesting, isn't it?. :-)