상속 된 클래스가 다른 반환 유형으로 가상 함수를 구현할 수 있습니까 (템플릿을 반환으로 사용하지 않음)?
답변:
경우에 따라 예, 반환 유형이 원래 반환 유형과 공변 하는 한 파생 클래스가 다른 반환 유형을 사용하여 가상 함수를 재정의하는 것이 합법적입니다 . 예를 들어 다음을 고려하십시오.
class Base {
public:
virtual ~Base() {}
virtual Base* clone() const = 0;
};
class Derived: public Base {
public:
virtual Derived* clone() const {
return new Derived(*this);
}
};
여기에서 를 반환하는 Base
라는 순수 가상 함수를 정의 clone
합니다 Base *
. 파생 된 구현에서이 가상 함수는 반환 형식을 사용하여 재정의됩니다 Derived *
. 반환 유형은 기본과 동일하지 않지만 언제든지 작성할 수 있기 때문에 완벽하게 안전합니다.
Base* ptr = /* ... */
Base* clone = ptr->clone();
에 대한 호출 clone()
은 항상 Base
객체에 대한 포인터를 반환합니다.를 반환 Derived*
하더라도이 포인터는 암시 적으로 a로 변환 가능 Base*
하고 작업이 잘 정의되어 있기 때문입니다.
보다 일반적으로 함수의 반환 유형은 서명의 일부로 간주되지 않습니다. 반환 유형이 공변이면 모든 반환 유형으로 멤버 함수를 재정의 할 수 있습니다.
Base*
과 long
와 Derived*
함께 int
(또는 다른 방법의 주위에, 문제가되지 않습니다). 작동하지 않습니다.
예. 반환 유형은 공변 인 한 다를 수 있습니다 . C ++ 표준은이를 다음과 같이 설명합니다 (§10.3 / 5).
재정의 함수의 반환 유형은 재정의 된 함수의 반환 유형과 동일하거나 함수 클래스와 공변 해야합니다. 함수가 함수를
D::f
재정의하는B::f
경우 다음 기준을 충족하면 함수의 반환 유형이 공변합니다.
- 둘 다 클래스에 대한 포인터 또는 클래스 98에 대한 참조 )
- 의 반환 유형에
B::f
있는 클래스는D::f
또는 의 반환 유형에있는 클래스와 동일한 클래스이고,의 반환 유형에있는 클래스 의 명확한 직접 또는 간접 기본 클래스이고에서D::f
액세스 할 수 있습니다.D
- 포인터 또는 참조는 모두 동일한 cv-qualification을
D::f
가지며의 반환 유형에있는 클래스 유형은의 반환 유형에있는 클래스 유형과 같거나 cv-qualification이 적거나 cv-qualification이 적습니다B::f
.
각주 98은 "클래스에 대한 다중 레벨 포인터 또는 클래스에 대한 다중 레벨 포인터에 대한 참조는 허용되지 않습니다."라고 지적합니다.
요컨대, if D
가의 하위 B
유형 인 경우 함수 D
의 반환 유형은에서 함수의 반환 유형의 하위 유형이어야합니다 B
. 반환 유형에 따라 자신을 때 가장 일반적인 예는 D
하고 B
있지만 될 필요가 없습니다. 두 개의 개별 유형 계층이있는 경우를 고려하십시오.
struct Base { /* ... */ };
struct Derived: public Base { /* ... */ };
struct B {
virtual Base* func() { return new Base; }
virtual ~B() { }
};
struct D: public B {
Derived* func() { return new Derived; }
};
int main() {
B* b = new D;
Base* base = b->func();
delete base;
delete b;
}
이것이 작동하는 이유는 호출자 func
가 Base
포인터를 기대 하기 때문 입니다. 모든 Base
포인터가 작동합니다. 따라서 D::func
항상 Derived
포인터를 반환하겠다고 약속하면 모든 Derived
포인터가 암시 적으로 Base
포인터 로 변환 될 수 있기 때문에 항상 조상 클래스가 배치 한 계약을 충족합니다 . 따라서 발신자는 항상 기대하는 것을 얻을 수 있습니다.
반환 유형을 변경하는 것 외에도 일부 언어에서는 재정의 함수 의 매개 변수 유형 도 변경할 수 있습니다. 그렇게 할 때 일반적으로 반 변성이 필요합니다 . 경우 즉, B::f
을 받아 Derived*
, 다음 D::f
a를 허용 할 것이다 Base*
. 후손들은 그들이 받아들이는 것에 대해 더 느슨해 지고 그들이 반환하는 것에 더 엄격 해질 수 있습니다 . C ++에서는 매개 변수 유형 반공 변성을 허용하지 않습니다. 매개 변수 유형을 변경하면 C ++는이를 새로운 함수로 간주하므로 오버로딩 및 숨기기가 시작됩니다. 이 주제에 대한 자세한 내용 은 Wikipedia의 공분산 및 반공 분산 (컴퓨터 과학) 을 참조하십시오 .
가상 함수의 파생 클래스 구현은 공변 반환 유형을 가질 수 있습니다 .