가상함수
2. 가상 함수
면접에나옴 !
이번엔 객체를 heap에 만들어 보겠습니다.
1) heap에 할당된 객체
결과를 보면 다소 이상한점을 확인할 수 있습니다.
바로 A* c = new B(); 부분 입니다.
생성자는 정상 호출됬지만 소멸자는 호출되지 않았습니다.
왜그럴까요??
new B()를 통해 B객체를 선언 했지만 포인터 타입을 A* 로 받았기 때문에
해당 포인터로 선언된 객체 c는 A클래스의 정보밖엔 볼 수 없습니다.
이해를 돕기위해 그림을 그려보겠습니다.
위 그림을 보면 이렇게 생각할 수 있습니다.
"포인터 자료형은 A라면서?? 근데 왜 heap에는 B객체의 맴버변수가 들어있냐?"
new B()로 할당받았기 때문입니다......
이런게 가능하느냐?? 가능합니다.
그리고 이런걸 upcasting 이라고 합니다. downcasting도 있지요.
또한 heap에 사용할 수 없는 자식의 맴버변수가 잡히는 이유도 downcasting으로 인해
접근이 가능해 지기 때문입니다.
정리하자면.
1. A* c = new B(); 에서 new로 생성된 자료형에 따라 해당 자료형에 맞게 메모리가 할당된다.
2. 할당은 되지만 포인터 자료형이 A 이므로 A클래스의 맴버 변수와 맴버 함수만 볼 수 있다.
3. 사용할 수 없는 자식클래스의 맴버 변수들까지 할당되는 이유는 downcasting을 위해서다.
(사실 new를 통해 B객체를 생성했으니 당연히 B클래스의 내용이 들어가지만, 이 부분을 햇갈려 하는 분들이
많기에 위 설명처럼 설명했습니다. 사실 맞는 말이기도 하구요.)
다시 본론으로 들어와서 생성자와 소멸자 호출을 보겠습니다.
A생성자 -> B 생성자 -> A 소멸자 순으로 호출이 됐습니다.
생성자는 A와 B가 호출됐는데, 왜 소멸자는 A만 호출이 되었을까??
이는 코드가 실행되면서 호출되는 관계를 살펴보면 됩니다.
코드에 new B(); 만 실행시켜 보세요. 출력결과는 다음과 같이 나올 것입니다.
A생성자
B생성자
이렇게 말이죠.
왜그런지 이해를 돕기위해 그림을 다시한번 가져와 보겠습니다.
때문에 upcast를 통한 객체 선언은 이런 문제점이 있습니다.
이를 해결하기 위해 virtual이라는 키워드가 등장합니다.
(드디어 나왔네요.... virtual...)
virtual 키워드를 함수에 붙여주면 해당 클래스에는 v-table이란 것이 생성되게 됩니다.
우선 코드를 살펴보겠습니다.
1) virtual 키워드가 붙은 소멸자
생성자와 소멸자가 정상적으로 호출된 것을 확인할 수 있습니다.
하지만 클래스의 크기가 좀 이상해 졌습니다.
원래대로 라면 A 클래스의 크기는 8바이트가 나와야 하고,
B클래스의 크기는 16바이트가 나와야 하는데 말이죠...
이는 v-table을 가르키는 virtual table pointer가 추가가 되서 그렇습니다.
v-table이 선언된 객체의 매모리 구조를 보겠습니다.
이렇게 됩니다.
"무슨 말이냐? B객체에는 virtual이라는 키워드가 없는데..."
부모 클래스에 virtual이 선언되면 자식 클래스는 자동으로 B클래스에 대한
virtual pointer를 만들게 됩니다.
그럼 A* c = new B(); 에 대한 메모리 구조를 그려보겠습니다. 디테일 하게....
이런식으로 구성됩니다. new B() 를 사용해 메모리를 할당했기 때문에 B객체에 대한 v-table이 생성되게 됩니다.
또한 B객체의 v-table엔 B클래스의 소멸자에 대한 주소정보를 담고 있죠.
출처 : https://hwan-shell.tistory.com/225
요약 : 예를들어 A* a = new B(); 이런식으로 자료형을 일치 시키지 않으면 소멸자가 생성되지않는데 이를 해결하기 위해서 가상함수를 써서 소멸자를 생성시킴으로써 최적화 하는듯