C++에서 Virtual이 붙은면 이 함수는 부모 클래스든 자식 클래스든 마지막으로 대체되는
함수로 호출이 되는 특성이 있다.
예를 들어
class AA
{
public:
virtual void func(){
cout<<"A Func"<<endl;
}
};
class BB:public AA
{
public:
void func(){
cout<<"B Func"<<endl;
}
}; 로 정의 되는 경우
BB* b=new BB
b->func 하게 되면 B Func가
AA* a=new BB
a->func를 하게 되도 B Func가 출력되게 된다.
이는 Virtual이라는 키워드가 붙은 함수에 대해서 이를 상속 받아서 구현한 최종의 함수가 호출됨을 의미한다. 측 a->func를 하게 되면 b->func를 대신 호출하게 된다.
만약 Class AA의 virtual 키워드를 없에게 되면
AA* a=new BB
a->func
를 하게 되면 A Func가 출력되며
BB* b=new BB
b->func
를 하게 되면 B Func가 출력된다
Virtual이 없는 경우 상속은 받은 클래스에서 overriding하게 되는 경우 부모의 함수를 가리게 되는데, 만약 부모의 포인터로 접근하게 되면 이 가림 효과가 없어지게 된다.
특이한 것은
생성자의 경우 부모의 생성자를 호출하고 자식의 생성자를 호출하게 되는데
소멸자의 경우는 Virtual 소멸자를 쓰지 않으면 부모 포인터로 자식 객체를 가르키는 경우에
delete하게 되면 부모의 소멸자만 호출된다는 것이다. 이는 메모리 리크 발생 위험이 있다
하나의 예를 들면
class AA
{
public:
AA(){ cout<<"A Con"<<endl; }
~AA(){ cout<<"A Des"<<endl;}
};
class BB:public AA
{
public:
BB(){ cout<<"B Con"<<endl; }
~BB(){ cout<<"B Des"<<endl;}
};
에서
AA* a=new BB();
delete a
를 하게 되면
A Con
B Con
A Des 가 출력된다.
이는 앞의 오버라이딩에서 Virtual이 붙은 경우와 그렇지 않은 경우에 자식 클래스를 부모의 포인터로 호출할 경우 일어나는 현상의 하나이다. 즉 AA의 소멸자에 virtual이 붙어 있지 않기 떄문에 a의 소멸자를 호출하는 상황에서는 BB의 소멸자가 가려 있지 않기 때문에 A의 소멸자만 호출되고 끝이난다.
따라서 이 경우 소멸자에 virtual을 붙여줘야 한다.
위 현상에서 virtual을 붙이게 되면 AA의 소멸자를 호출하게 될 경우 virtual의 특성상 오버라이딩 된 BB의 소멸자를 호출하고, BB의 소멸자는 AA의 클래스를 상속하고 있기 때문에 AA의 소멸자를 호출한다. 따라서 BB의 소멸자 호출 후 AA의 소멸자가 호출되는 것이다..