개요
가상 함수(virtual funcion)란, 자식 클래스에서 재정의할 것으로 예상되는 멤버 함수를 의미한다.
가상 함수는 자신을 호출하는 객체의 타입에 따라 실제 호출할 함수가 결정된다.
부모 클래스에서 virtual을 사용해 가상 함수를 선언하면, 자식 클래스에서 재정의된 멤버 함수도 자동으로 가상 함수가 된다.
선언
virtual FunName();
동적 바인딩
C++ 컴파일러가 함수를 호출할 때 함수가 어느 블록의 어느 메모리 위치에 있는지 정확히 알고 있어야 한다.
대부분의 함수를 호출하는 코드는 컴파일 타임에 고정된 메모리 주소로 변환된다.
이것을 정적 바인딩(static binding), 초기 바인딩(early binding)이라고 한다.
C++에서 가상 함수가 아닌 멤버 함수는 정적 바인딩을 따른다.
가상 함수는 앞서 말했듯 객체의 타입에 따라 실체 호출할 함수가 달라지므로, 컴파일 타임에 바인딩할 수 없다.
따라서 런 타임에 올바른 함수의 메모리 주소로 바인딩해줘야 하고, 이를 동적 바인딩(dynamic binding), 지연 바인딩(late binding)이라고 한다.
가상 함수도 타입이 분명할 때에는 정적 바인딩을 한다. 포인터나 참조를 통해 호출될 때만, 동적 바인딩을 한다.
가상 함수 테이블
컴파일러가 가상 함수를 다루는 가장 일반적인 방식은 가상 함수 테이블(virtual function table)을 이용하는 것이다.
C++ 컴파일러는 각각의 객체마다 가상 함수 테이블을 가리키는 포인터를 저장하기 위한 숨겨진 멤버를 하나씩 추가한다.
가상 함수를 단 하나라도 가지는 클래스에는 가상 함수 테이블이 추가된다.
가상 함수를 호출하면, C++ 프로그램은 가상 함수 테이블에 접근하여 필요한 함수의 주소를 찾는다.
이처럼 가상 함수를 사용하면 호출 과정이 복잡해지므로, 메모리 및 실행 속도 측면에서 오버헤드가 존재한다.
따라서 C++에서 기본 바인딩은 정적 바인딩이고, 필요한 경우에만 가상 함수를 선언하길 권장하고 있다.
가상 소멸자
Parent* p = new Child;
...
delete p;
위 코드에서 Parent는 Child의 부모 클래스이므로 p라는 Child 객체가 동적으로 할당된다.
이때 부모 클래스의 소멸자를 가상 함수로 선언해주지 않으면, 마지막 구문에서 부모 클래스(Parent)의 소멸자는 호출이 되지만, 자식 클래스(Child)의 소멸자는 호출되지 않는다.
이렇게 되면 Child 객체에 동적으로 할당된 메모리가 정상적으로 해제되지 않아 문제가 발생한다.
따라서 C++에서 자식 클래스의 소멸자를 호출하려면, 부모 클래스의 소멸자는 반드시 가상으로 선언해줘야 한다.
부모 클래스의 소멸자에서 아무 일도 하지 않더라도, 명시적으로 소멸자를 선언할 필요가 없어도 가상 소멸자를 선언해야 한다.
참고
http://www.tcpschool.com/cpp/cpp_polymorphism_virtual
https://hwan-shell.tistory.com/225
'언어 > C++' 카테고리의 다른 글
[C++] string 한글 출력 (0) | 2022.07.15 |
---|---|
[C++] 프로그램 실행 순서 (0) | 2022.07.13 |
[C++] Vector 원소 삭제(erase, remove) (0) | 2022.07.05 |
인수 목록이 일치하는 생성자의 인스턴스가 없습니다. 인수형식이 const char입니다. 오류 (0) | 2022.03.10 |
[C++] 깊은 복사와 얕은 복사 (0) | 2022.03.08 |