#30. 오버라이딩, virtual, 객체포인터
#상속 관계에서 생성자와 소멸자
상속 관계에서 생성자와 소멸자는 아래와 같이 이루어 진다.
부모클래스 생성자 -> 자식클래스 생성자 -> 자식클래스 소멸자 -> 부모클래스 소멸자
순으로 생성자와 소멸자가 생성된다.
메모리 할당 순서까지 확인해보면
객체 생성 -> 메모리 할당 -> 부모 생성자 호출 -> 자식 생성자 호출
-> 자식 소멸자 호출 -> 부모 소멸자 호출 -> 메모리 반환 -> 객체 소멸
순이다.
#객체포인터
객체 자신의 주소 또는 자신을 직/간접 상속받는 객체의 주소를 저장할 수 있다.
부모클래스* AA = new 부모클래스; -> 가능
부모클래스* AA2 = new 자식클래스; ->가능
자식클래스* AA3 = new 부모클래스; -> 불가능
#오버라이딩
상속관계에서 부모클래스의 멤버함수를 자식클래스에서 재정의 하는것을 오버라이딩이라고 한다.
함수 정의를 할 때, 반환 타입, 함수 이름, 매개변수 정보가 변하지 않는다.
둘다 Func()라고 가정 할 때,
부모클래스* ptr = new 자식클래스;
ptr->Func();
를 했을때 타입 기준의 함수 즉 부모클래스의 Func가 나오게 된다.
#가상함수 (virtual)
가상함수는 클래스를 번역하면서 virtual이 있으면 가상함수 테이블을 만든다
그 테이블(배열형식)에 가상함수들을 저장하고
그 주소를 가지는 가상함수포인터를 멤버로 추가한다.
따라서 함수만 가지고 있는 경우 클래스의 크기는 1byte이고
가상함수를 가지고 있는 클래스의 크기는 4byte로 나오게 된다.
가상함수는 상속에도 적용이 되는데
부모클래스 virtual void Func() / 자식클래스 void Func()
있을경우
자식클래스의 Func도 virtual 화 된다.
virtual은 처음 불러올때 없는 취급을 하다가 없으면 가상함수포인터의 주소를 불러오는 형식으로 작동이 된다.
위의 오버라이딩에서
부모클래스* ptr = new 자식클래스;
ptr->Func();
일 경우
ptr은 부모클래스 타입이라 부모클래스의 Func()를 불러왔지만,
virtual을 적용하게 되면 기존의 생성자와 소멸자 순으로 Func을 확인하게 된다.
부모생성->자식생성->자식생성->자식소멸
순인데
1. 이때 처음 부모클래스의 Func을 확인하고 virtual 화 되어있어서
2. 자식 Func을 확인하고 virtual 화 되어있어서
3. 자식 가상함수포인터의 주소를 참조하게 된다.
따라서 위와 같은경우 virtual 함수를 부를때 자식 클래스의 Func()를 부르게 된다.
사용방법
반환타입 앞에 virtual을 붙이면 된다.
ex) virtual void Func();
#순수가상함수
순수 가상함수는 부모클래스에서 함수 앞에 virtual을 붙이고 정의부를 구현 안한 것을 말한다.
이 가상함수는 선언부 끝에 =0을 붙여서 사용 할 수 있다.
무조건적으로 자식클래스에서 다형성을 이용하여 사용하게 하기 위함이다
virtual void Func()=0; 으로 선언하면 된다.
-> 순수 가상함수를 단 하나라도 가지고 있는 클래스(추상클래스)의 경우 객체를 생성할 수 없다.
이때 자식클래스에서 오버라이딩을 하지 않을경우 순수 가상함수를 상속받아
자식클래스에서도 객체를 생성할 수 없게 된다
#객체 포인터의 문제점
상속관계에서
부모클래스* ptr = new 자식클래스;
를 하면 부모클래스 생성자 및 자식클래스 생성자가 정상적으로 나오게 된다.
다 사용하고 난 뒤
delete ptr;
을 할 경우
ptr은 부모클래스의 형식이기 때문에 부모클래스의 소멸자만 호출이 되고 끝이 나버린다.
이때 virtual을 부모소멸자에 사용하게 되면 위의 virtual 의 방식대로 자식소멸자 -> 부모소멸자 모두 정상적으로 호출되게 된다.