파괴자를 직접 부르면 어떻게 될까?
object o;
o.~object();
궁금해졌다.
-----------------------------------------------------------------------------
#include <windows.h>
#include <tchar.h>
#include <iostream>
class o_1
{
public:
o_1 () { std::cout << "o_1()" << std::endl; }
virtual ~o_1() { std::cout << "~o_1()" << std::endl; }
};
class o_2 : public o_1
{
public:
o_2 () : o_1() { std::cout << "o_2()" << std::endl; }
virtual ~o_2() { std::cout << "~o_2()" << std::endl; }
};
class o_3 : public o_2
{
public:
o_3 () : o_2() { std::cout << "o_3()" << std::endl; }
virtual ~o_3() { std::cout << "~o_3()" << std::endl; }
};
void test1 (void)
{
o_3 o;
o_1* p = &o;
std::cout << "-----------" <<std::endl;
p->~o_1 ();
std::cout << "-----------" <<std::endl;
}
void test2 (void)
{
o_1* p = new o_3 ();
std::cout << "-----------" <<std::endl;
p->~o_1 ();
std::cout << "-----------" <<std::endl;
delete p;
}
void test3 (void)
{
o_1* p = new o_3 ();
std::cout << "-----------" <<std::endl;
//p->~o_1 ();
std::cout << "-----------" <<std::endl;
delete p;
}
int _tmain(int argc, _TCHAR* argv[])
{
std::cout << "###########" <<std::endl;
test1();
std::cout << "###########" <<std::endl;
test2();
std::cout << "###########" <<std::endl;
test3();
std::cout << "###########" <<std::endl;
return 0;
}
-----------------------------------------------------------------------------
########### <- test1() 함수: 잘된다...
o_1()
o_2()
o_3()
-----------
~o_3()
~o_2()
~o_1()
-----------
~o_3()
~o_2()
~o_1()
########### <- test2() 함수: 무엇인가 이상하다???
o_1()
o_2()
o_3()
----------- <- p->~o_1 (); 호출 부분... 부모 클래스 파괴자를 호출한다.
~o_3()
~o_2()
~o_1()
----------- <- delete p; 가 이상하다 = _=??? 무엇일까...
~o_1()
########### <- test3() 함수:
o_1()
o_2()
o_3()
----------- <- //p->~o_1 ();
----------- 주석처리 했더니 delete p; 가 제대로 동작한다.
~o_3()
~o_2()
~o_1()
###########
계속하려면 아무 키나 누르십시오 . . .
-----------------------------------------------------------------------------
디버깅을 해보았다.
p->~o_1(); 호출하기전 __vfptr(virtual fucntion pointer) 녀석은 이렇게 가리키고 있었다.
하지만, p->~o_1();을 호출하니 __vfptr 녀석은 이렇게 바뀌어 버렸다.
test1()역시 p->~o_1();를 수행하면 p의 __vfptr 값도 동일하게 바뀌었다.
고심끝에 내린 결론은
test1()에서 Stack에 생성된 객체는 파괴될때 가상함수-파괴자를 사용하지 않고 파괴 되므로
위와같은 결과를 얻었고,
test2()에서 Heap에 생성된 객체는 파괴되기전에 직접 p->~o_1(); 호출 하여 __vfptr이
변경됨으로서 delete p;에서 가상함수-파괴자를 이용한 파괴자 호출이 제대로 되지 못하는
것이다.
__vfptr이 변경 되므로 가상함수들이 제대로 동작을 안할 것이라 판단 되었다.
그래서, 소스 코드를 수정해서 다시 테스트 해보았다.
-----------------------------------------------------------------------------
#include <windows.h>
#include <tchar.h>
#include <iostream>
class o_1
{
public:
o_1 () { std::cout << "o_1()" << std::endl; }
virtual ~o_1() { std::cout << "~o_1()" << std::endl; }
public:
virtual void show (void)
{
std::cout << "o_1::show()" << std::endl;
}
};
class o_2 : public o_1
{
public:
o_2 () : o_1() { std::cout << "o_2()" << std::endl; }
virtual ~o_2() { std::cout << "~o_2()" << std::endl; }
public:
virtual void show (void)
{
std::cout << "o_2::show()" << std::endl;
}
};
class o_3 : public o_2
{
public:
o_3 () : o_2() { std::cout << "o_3()" << std::endl; }
virtual ~o_3() { std::cout << "~o_3()" << std::endl; }
public:
virtual void show (void)
{
std::cout << "o_3::show()" << std::endl;
}
};
void test1 (void)
{
o_3 o;
o_1* p = &o;
std::cout << "-----------" <<std::endl;
p->show ();
o.show();
std::cout << "-----------" <<std::endl;
p->~o_1 ();
std::cout << "-----------" <<std::endl;
p->show ();
o.show();
std::cout << "-----------" <<std::endl;
}
void test2 (void)
{
o_1* p = new o_3 ();
std::cout << "-----------" <<std::endl;
p->~o_1 ();
std::cout << "-----------" <<std::endl;
delete p;
}
void test3 (void)
{
o_1* p = new o_3 ();
std::cout << "-----------" <<std::endl;
//p->~o_1 ();
std::cout << "-----------" <<std::endl;
delete p;
}
int _tmain(int argc, _TCHAR* argv[])
{
std::cout << "###########" <<std::endl;
test1();
std::cout << "###########" <<std::endl;
test2();
std::cout << "###########" <<std::endl;
test3();
std::cout << "###########" <<std::endl;
return 0;
}
-----------------------------------------------------------------------------
########### <- test1() 함수: 잘되는게 아니었다.
o_1()
o_2()
o_3()
-----------
o_3::show() <- p->show (); 정상이다.
o_3::show() <- o.show(); 정상이다.
-----------
~o_3()
~o_2()
~o_1()
-----------
o_1::show() <- p->show (); __vfptr이 변경되었으므로 제대로 동작을 못한다.
o_3::show() <- o.show();
-----------
~o_3()
~o_2()
~o_1()
########### <- test2() 함수:
o_1()
o_2()
o_3()
-----------
~o_3()
~o_2()
~o_1()
-----------
~o_1()
########### <- test3() 함수:
o_1()
o_2()
o_3()
-----------
-----------
~o_3()
~o_2()
~o_1()
###########
계속하려면 아무 키나 누르십시오 . . .
-----------------------------------------------------------------------------
처음에는 컴파일러 버그라고 생각되었지만 곰곰히 따져보니 아닌 것 같다.
설마, 진짜 컴파일러 버그는 아니겠지... = _ =;
[사용 컴파일러]
Microsoft Visual Studio 2005
Version 8.0.50727.762 (SP.050727-7600)
Microsoft .NET Framework
Version 2.0.50727 SP1
Installed Edition: Professional
-----------------------------------------------------------------------------
추가:
데브피아에 올렸보았었는데 오해하실 수 있는 부분 있는거 같아서 추가합니다.
제가 말하고 싶었던것은 소멸자를 직접 호출했을경우 vftable을 가리키는 __vfptr이
변경된다는 걸 말하고 싶었던겁니다.
좀 더 부연설명을 하자면 메모리 풀 같은 걸 만들때 메모리 풀에서 받은 객체를 사용하고 삭제
후 그대로 또 사용했을 경우 일어나는 현상이 어떻게 될것인가 따져보는 의미입니다.
위의 코드는 일종의 toy code입니다.
제가 컴파일러 버그가 아닐까라고 생각한 부분은 직접 파괴자(소멸자)가 호출된다고 해서
메모리에서 실제로 delete가 일어나는 건 아니기 때문에 vftable을 가리키는 __vfptr가
변경되는게 맞는건지 아닌지 잘 판단이 안되었기 때문에 적어 놓은겁니다.