1. Upcasting과 Downcasting
일반적인 경우, 포인터 변수에 다른 type의 포인터를 담을 수 없다.
(예외: void* 변수는 다른 type의 포인터를 담을 수 있다.)
하지만 "부모 클래스"의 포인터에 "자식 클래스"를 담을 경우, 자식 클래스는 부모 클래스를 물려받았기 때문에 메모리 앞부분이 부모 클래스와 똑같다. 따라서 포인터 변수로 담을 수 있으며, 이를 upcasting이라 한다.
이때 부모 클래스와 자식 클래스에 같은 이름의 멤버가 있다면, 원래 변수가 자식 클래스였어도 upcasting 된 변수를 통해 접근할 때는 무조건 부모 클래스의 멤버가 불리게 된다.
부모 클래스에서 자식 클래스의 멤버를 부르고 싶으면, 자식클래스로 형변환을 한 후에 불러주면 된다. 이를 downcasting 이라 부른다.
Downcasting을 static_cast<자식클래스>(변수) 로 하게 되면 실행 시간에 변수가 원래 자식 클래스인지, 아니면 부모 클래스인지 확인이 되지 않기 때문에, 정의되지 않은 동작을 할 수 있다. (실제 자식클래스가 아닌데 자식 클래스의 멤버를 부른다)
Downcasting을 dynamic_cast<자식클래스>(변수)로 해줘야 실행 시간에 변수의 type을 체크해서 변수가 원래 자식 클래스였으면 downcasting이 성공하고, 그렇지 않다면 실패하여 null을 반환시킨다. 따라서 dynamic_cast를 사용하는 것이 더 안전하다.
단 그냥 dynamic_cast를 할 경우 "source type is not polymorphic" 에러가 발생하는데, 이를 막기 위해선 person의 소멸자를 virtual로 지정해주어야 한다.
2. 함수 오버라이드 (function override)
위의 경우에서 downcasting 없이 자식클래스의 함수를 부르고 싶으면, 부모 클래스에서 멤버 함수를 가상 함수 (virtual function)으로 정의하면 된다.
이 경우 부모 클래스에서 함수를 선언할 때 virtual 키워드를 앞에 붙인다. (구현부에는 붙이지 않음)
그리고 자식 클래스에서 재정의 할 경우에는 virtual 키워드를 붙여도 되고 붙이지 않아도 된다. (가독성 때문에 붙이는 것이 좋다.)
3. 추상 클래스 (Abstract class)
가상 함수에 정의 대신 = 0; 으로 구현하지 않은 경우를 순수 가상함수라고 하고, 순수 가상 함수를 가진 클래스를 추상 클래스라고 한다.
추상 클래스는 순수 가상함수 때문에 객체를 만들 수 없으며, 오직 파생클래스에서 순수가상함수를 구현한 경우에만 객체를 만들 수 있다.
4. 가상 소멸자
아래와 같이 부모 클래스의 포인터로 자식 클래스를 생성하고나서 반환하는 경우를 생각해 보자.
이때 Line 14에서 p가 소멸될 때 p는 person의 pointer이므로, ~student()를 호출하는 것이 아닌 ~person()을 호출하게 되고, student의 자원에 대해 소멸자가 불리지 않게 된다.
이 문제를 해결하기 위해서는 ~person()을 가상 함수로 선언해 주어서, 자식 클래스의 소멸자가 불리게 해야 한다. (그리고 나서 부모 클래스의 소멸자도 불리게 된다.)
이 경우 자식 클래스의 소멸자는 명시하지 않아도 가상 함수가 된다.
따라서, 클래스를 상속할 경우 반드시 소멸자를 가상함수로 만들어야 한다.
'프로그래밍 > C++' 카테고리의 다른 글
템플릿 (Template) (0) | 2018.11.04 |
---|---|
함수 바인딩 (0) | 2018.10.29 |
상속 (Inheritance) (0) | 2018.10.22 |
연산자 재정의 (Operator overloading) (0) | 2018.07.01 |
this 포인터 (0) | 2017.11.13 |