수업/C++
11. 상속- 가상함수(virtual),업캐스팅,다운캐스팅
MDanderson
2024. 11. 28. 22:10
정적연결
Person이 부모 Student클래스가 자식일 떄
Person *pPt = &harry; ( 자식클래스)
일때
pPt ->print(); 하면 부모클래스의 print함수가 작동한다.
즉, 포인터의 자료형을 가지고 판단한다.
그런데 연결된 객체가 Student객체이니까 Student객체의 print함수를 쓸려면,
(Student *) 를 통해서 포인터의 클래스를 바꿔준다. 즉 형변환 해줌.
이방법은 위험함. p2가 Student객체를 가리키고 있으리라는 보장이없다.
동적연결
소멸자를 가상함수로 선언하는 이유
- Base* obj = new Derived();:
- Base 클래스 포인터로 Derived 객체를 생성합니다.
- delete obj;:
- Base의 소멸자는 호출되지만, Derived의 소멸자는 호출되지 않습니다.
- 이는 소멸자가 가상 함수가 아니기 때문에, 컴파일러가 정적 바인딩(static binding)을 사용하기 때문입니다.
소멸자를 가상 함수로 선언하지 않아도 되는 경우
- 다형성이 사용되지 않는 경우:
- 기초 클래스 포인터로 파생 클래스 객체를 다루지 않을 경우, 소멸자를 가상 함수로 선언하지 않아도 됩니다.
- 상속이 없는 클래스:
- 상속 계층이 없는 단독 클래스라면, 소멸자를 가상으로 선언할 필요가 없습니다.
4번째줄은 다운캐스팅인데 묵시적 형변환이 안되니까 에러남
- static_cast<Student*>를 사용하여 pPrsn2를 Student*로 변환.
- 이 변환은 컴파일 시점에 타입 검사 없이 허용되지만, 런타임에 pPrsn2가 실제로 Student 객체가 아니라면, **정의되지 않은 동작(Undefined Behavior)**이 발생합니다.
- pStdnt2->getSchool():
- pStdnt2는 실제로 Student 객체가 아닌 Person 객체를 가리키고 있으므로, 잘못된 메모리 접근이 발생하여 프로그램이 비정상 종료되거나 예측할 수 없는 동작이 발생할 수 있습니다.
왜 getSchool()을 호출할 수 없는가?
pPrsn2는 실제로 Student 객체가 아니기 때문:
- static_cast는 **런타임 타입 정보(RTTI)**를 검사하지 않기 때문에, pPrsn2가 실제로 Student 객체인지 확인하지 않습니다.
- 결과적으로, pPrsn2는 여전히 Person 객체를 가리키지만, 타입만 강제로 Student*로 바꿔버립니다.
- pStdnt2->getSchool()을 호출하려 하면, 메모리에서 잘못된 위치를 참조하게 되어 정의되지 않은 동작이 발생합니다.
해결 방법: 안전한 다운캐스팅 (dynamic_cast)
static_cast 대신 **dynamic_cast**를 사용하면 런타임 타입 검사가 이루어져, 캐스팅이 안전한지 확인할 수 있습니다. dynamic_cast는 타입이 맞지 않을 경우 nullptr를 반환합니다.
pStdnt2가 dynamic_cast<Student*>(pPrsn2)를 통해 변환된 결과라면, 실제로 pPrsn2가 Student 객체를 가리키고 있지 않다면 **pStdnt2는 nullptr(즉, false)**가 됩니다.
dynamic_cast 동작 원리
- dynamic_cast는 RTTI(런타임 타입 정보)를 사용하여, 실제로 pPrsn1이 Student 객체를 가리키는지 확인합니다.
- 타입이 맞지 않으면 **nullptr**를 반환하므로, 안전하게 다운캐스팅 여부를 확인할 수 있습니다.
- 문제점: static_cast<Student*>는 런타임 타입 검사를 하지 않으므로, 잘못된 다운캐스팅 시 정의되지 않은 동작이 발생합니다.
- 해결 방법: 다운캐스팅 시 **dynamic_cast**를 사용하여 런타임에 타입 검사를 수행하고, 캐스팅 안전성을 보장하세요.
- 기억할 점: dynamic_cast는 virtual 소멸자가 있어야 작동합니다. 기초 클래스에 항상 가상 소멸자를 정의하세요.
Person 클래스에 가상 소멸자(virtual destructor) 또는 다른 가상 함수를 추가하면, RTTI가 활성화되고 dynamic_cast를 사용할 수 있습니다.