계승
상속의 전체 지점은 파생 클래스의 인스턴스가 다른 파생 유형의 다른 인스턴스와 동일하게 처리 될 수 있도록 여러 가지 구현간에 공통 인터페이스와 프로토콜을 공유하는 것입니다.
C ++ 상속에서는 구현 세부 정보를 제공하므로 소멸자를 가상으로 표시 (또는 표시하지 않음)는 구현 세부 정보 중 하나입니다.
함수 바인딩
이제 함수 또는 생성자 또는 소멸자와 같은 특수한 경우를 호출 할 때 컴파일러는 어떤 함수 구현을 의미하는지 선택해야합니다. 그런 다음이 의도를 따르는 기계 코드를 생성해야합니다.
이 작업을 수행하는 가장 간단한 방법은 컴파일 타임에 함수를 선택하고 충분한 코드를 생성하여 값에 관계없이 해당 코드가 실행될 때 항상 함수에 대한 코드를 실행하도록하는 것입니다. 상속을 제외하고는 잘 작동합니다.
함수가있는 기본 클래스가 있고 (생성자 또는 소멸자를 포함한 모든 함수 일 수 있음) 코드에서 함수를 호출하면 이것이 무엇을 의미합니까?
예를 들어 initialize_vector()
컴파일러를 호출 한 경우에서 찾은 Base
구현 또는에서 구현 을 실제로 호출 할 것인지 결정해야합니다 Derived
. 이를 결정하는 두 가지 방법이 있습니다.
- 첫 번째는
Base
유형 에서 호출했기 때문에 의 구현을 의미한다고 결정하는 것입니다 Base
.
- 두 번째는에 저장된 값의 실행시의 형태 때문 결정하는 것입니다
Base
입력 된 값이 될 수있다 Base
, 또는 Derived
(가 호출 될 때마다) 호출 될 때 그 결정으로 만들 수있는 호출에, 실행시에해야합니다.
이 시점의 컴파일러는 혼란스럽고 두 옵션 모두 동일하게 유효합니다. 이것은 virtual
믹스에 올 때 입니다. 이 키워드가 있으면 컴파일러는 옵션 2를 선택하여 코드가 실제 값으로 실행될 때까지 가능한 모든 구현 간의 결정을 지연시킵니다. 이 키워드가 없으면 컴파일러는 옵션 1을 선택합니다. 그렇지 않으면 정상적인 동작이기 때문입니다.
가상 함수 호출의 경우 컴파일러는 여전히 옵션 1을 선택할 수 있습니다. 그러나 이것이 항상 사실임을 증명할 수있는 경우에만.
생성자와 소멸자
그렇다면 가상 생성자를 지정하지 않는 이유는 무엇입니까?
더 직관적으로 어떻게 컴파일러는 생성자의 동일한 구현에 선택할 것 Derived
와 Derived2
? 이것은 매우 간단합니다. 컴파일러가 실제로 의도 한 것을 배울 수있는 기존의 가치는 없습니다. 생성자의 역할이기 때문에 기존 값은 없습니다.
그렇다면 왜 가상 소멸자를 지정해야합니까?
좀 더 직관적으로 컴파일러는 구현 방식 Base
과 구현 방식 중 어느 것을 선택 Derived
합니까? 그것들은 단지 함수 호출이므로 함수 호출 동작이 발생합니다. 선언 된 가상 소멸자가 없으면 컴파일러는 Base
런타임 값 값에 관계없이 소멸자에 직접 바인딩하기로 결정 합니다.
많은 컴파일러에서 파생 된 데이터 멤버를 선언하지 않거나 다른 형식을 상속하지 않으면의 동작 ~Base()
이 적합하지만 보장되지는 않습니다. 아직 발화되지 않은 화염 방사기 앞에 서있는 것처럼 순전히 상황에 따라 작동합니다. 당신은 한동안 괜찮습니다.
C ++에서 기본 또는 인터페이스 유형을 선언하는 유일한 올바른 방법은 가상 소멸자를 선언하여 해당 유형의 유형 계층 구조의 지정된 인스턴스에 대해 올바른 소멸자를 호출하는 것입니다. 이를 통해 인스턴스에 대해 가장 잘 알고있는 함수가 해당 인스턴스를 올바르게 정리할 수 있습니다.
~derived()
vec의 소멸자를 위임 하는 언어를 제공한다 . 또는unique_ptr<base> pt
파생 된 소멸자를 알고 있다고 가정합니다 . 가상 방법이 없으면 이럴 수 없습니다. unique_ptr에 런타임 표현이없는 템플리트 매개 변수 인 삭제 함수가 제공 될 수 있지만 해당 기능은이 코드에 사용되지 않습니다.