virtual function (가상함수) 오버라이딩 두가지 규칙

1. 상속된 메소드를 재정의할 경우에는 오리지널 원형과 정확히 일치시킬 필요가 있음

(에외: 리턴형의 공변(covariance)- 리턴형이 기초 클래스에 대한 참조나 포인터인 경우 파생 클래스에 대한 참조나 포인터로 대체)

2. 기초 클래스 선언이 오버로딩 되어있다면, 파생 클래스에서 모든 기초 클래스 버전들을 재정의 해야 

 

Abstract class (추상 클래스) 의 특징

적어도 하나의 순수(pure) 가상함수를 가진 클래스

순수가상함수(pure virtual function) : 함수 선언 뒤에 “=0” 붙여서 표현하며 선언만으로 존재

순수가상함수를 포함한 클래스는 객체생성 불가능(기초역할을 하기위해서만 존재)

 

상속과 오버라이딩(Inheritance and Overriding)

기초 클래스 메소드를 파생 클래스에서 재정의 할때는 일반적으로 해당 기초 클래스 메소드를 virtual 로 선언함

virtual 키워드 사용하지 않을 경우, 프로그램은 참조형이나 포인터형에 기초하여 메소드를 선택

virtual 키워드 사용할 경우, 프로그램은 참조나 포인터에 의해 지시되는 객체형에 기초하여 메소드 선택

 

※ 주의: 파생 클래스에 하나라도 오버라이딩 된 함수가 존재할 경우, 기초 클래스에 가상 소멸자 선언해줘야 함

virtual 소멸자가 아니라면, 포인터형에 해당하는 소멸자만 호출될 것

(즉, 파생 클래스를 지시하는 경우에도 기초 클래스 소멸자가 호출된다는 것을 의미)

따라서 virtual 소멸자로 객체형에 해당하는 소멸자가 호출된 이후 기초 클래스 소멸자가가 자동으로 호출되게함

 

정적 결합과 동적 결합(Static binding & Dynamic binding)

 

정적 결합: 컴파일 동안 일어나는 결합 (초기 결합)

동적 결합: 런타임 동안 올바른 가상 메소드가 선택되도록 하는 결합(말기 결합)

 

객체에 대한 참조를 사용하여 또는 객체를 지시하는 포인터를 사용하여 가상 메소드가 호출되면,

프로그램은 그 참조나 포인터형을 위해 정의된 메소드를 사용하지 않고, 

객체형을 위해 정의된 메소드를 사용하며 이것을 “동적 결합”이라고 함

 

기초 클래스 포인터나 참조가 파생 클래스 객체를 지시하는 것이 항상 가능하므로 이러한 로직이 중요함

(암시적) 업캐스팅: 기초 클래스 포인터나 참조가 기초 클래스 객체나 파생 클래스 객체를 참조하는 것을 가능하게 함

 

동적결합이 필요한 이유

이에 대한 해답으로 가상 멤버 함수 개념이 등장

 

friend (프렌드) 와 virtual function 가상함수)

프렌드는 가상함수가 될 수 없음

멤버 함수만 가상함수가 될 수 있는데, 프렌드는 클래스 멤버가 아니기 때문

이 경우 프렌드 함수 내부적으로 가상 멤버 함수를 사용하게 하면됨

 

가상함수를 다시 정의하지 않으면, 파생 클래스는 그 함수의 기초 클래스 버전을 사용함

(길게 이어진 파생 사슬 구조라면 가장 최근에 정의된 버전을 사용함)

 

어떤 함수를 파생 클래스에서 재정의하면 동일한 함수 시그니처를 갖고 있는 기초 클래스 선언만 가리는 것이 아니라,

매개변수 시그니처와 상관 없이 같은 이름을 가진 모든 기초 클래스 메소드를 가림

 

 

가상함수 테이블의 생성 및 참조

virtual table 는 컴파일러에 의해 생성됨

생성하는 조건은 클래스 내부에 최소 하나의 가상함수가 정의 되어 있는 경우임

가상함수 테이블은 해당 클래스의 가상함수 리스트를 포함하고 있는 자료 구조를 의미함

 

가상함수를 포함하고 있는 클래스의 각 객체들은 가상함수 테이블을 가리키는 포인터를 가지는데,

이 포인터는 객체의 생성자에 의해 초기화 되며, 해당 클래스의 가상함수 테이블을 가리킴

 

객체에 의해 가상함수가 호출되는 것은, 가상함수 테이블 포인터를 이용하여 가상함수 테이블 내에 호출된 함수를 찾아 호출하게 되는 것

 

가상함수 테이블은 객체 포인터나 참조에서 가상함수가 호출되는 런타임 때에 참조 됨

컴파일러는 가상함수 테이블 포인터를 사용하는 코드를 생성함으로써, (객체의 실제 타입을 근거로 )정확한 함수를 찾아내게 됨

이러한 과정을 dynamic dispatch 라고 하며, 이 과정을 통해 런타임에 객체의 실제 타입으로부터 정확한 함수가 호출될 수 있게 되는 것임

 

가상함수 및 테이블의 사용은 C++의 핵심 기술중 하나인 다형성(Polymorphism)을 지원함으로써 유연하고 확장성 있는 코드를 구성할 수 있음

 

결론: virtual table 자체는 컴파일 타임(compile time)동안에 생성되나 조회는 런타임(runtime)에 이루어짐

+ Recent posts