[[ blog 이사 과정에서 정확한 posting날짜가 분실됨. 년도와 분기 정도는 맞지 않을까? ]]

In general, handling exception smoothly is very difficult. So, in many software, there are lots of prematured routine for exception handling. My recommend is, "just put "Assert" instead of premature exception handling(henceforth PEH)".

PEH give noisy information when exception is occurred, because software crashes at unexpected place due to PEH (Not the place where exception is actually raised).
Let's see following codes.

...
info = Get_personal_info(name);
if(!info){ return SYS_Fail;}
...
Personal_InfoT* Get_personal_info(char* name)
{
    ...
    int index = Search_with_name(name);
    if(index < 0) {return NULL;} // fail. (exception handling part)  ---- (*)
    ...
}

In this case, the place where exception is raised at first is (*). But, to handling exception, this function returns just 'NULL'. And at caller, it also returns 'SYS_Fail'. OK. It's reasonable. But, in practice, handling all these returned error smoothly requires lots of costs. So, in many cases, this is handled prematurely. And eventually, this error leads to software crash at unexpected place.

So, again, my point is just put 'Assert'. After all, we should try to debug all errors. 'Assert' is very helpful in debug. And then, we should decide whether remaining as 'Assert' or handling exception later - usually, exception from IO should be handled. This is more efficient in my opinion.

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

[[ blog 이사 과정에서 정확한 posting날짜가 분실됨. 년도와 분기 정도는 맞지 않을까? ]]

We can use 'typename' in template class.

template
class C
{
    typename T::bT* a;
}

But, keyword 'typename' cannot be used outside template. So, following is incorrect syntax.

class A;
class B
{
    A*     a;
    typename A::bT* b; // error here!
};

So, in this case, class A's definition should be included before definition of class B.

It's just small information about using 'typename'.

+ Recent posts