[[ 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?. :-)

+ Recent posts