가상 함수 코드를 보다가, 우연히 부모클래스의 가상 함수와 자식클래스의 가상 함수의 리턴 타입이 다르다는 것을 본 적이 있습니다.
혹시, 부모 클래스에서 상속 받은 가상 함수의 리턴 값이 달라도 상관 없다는 것을 아시나요?
물론, 이 포스팅을 검색해서 들어오셨다면, 그 사실을 모르셨거나, 정확한 이유가 궁금해서겠죠.
먼저, 답부터 말하면 가능합니다.
그런데, 일반적으로 오버라이딩을 공부하신 분이라면 반환 타입이 같아야 한다는 것은 모두 아는 사실인데, 그럼 불가능한 것이 아닌가라고 생각하실 수 있습니다.
그래서 열심히 구글링해본 결과, 부모 클래스의 함수 타입에 대해서 상속받은 클래스의 함수가 대체가능하다면 반환타입이 달라도 된다고 합니다.
말로만 설명하면 이해하기 어려우므로, 바로 간단한 예제로 설명하도록 하겠습니다.

|
부모 클래스와 자식 클래스의 가상 함수 반환타입이 같은 경우. |
아래 예제는 자기 자신의 객체를 만들어서 반환하는 clone() 함수를 만드는 예제입니다.
#include <iostream>
class SuperClass {
public:
virtual SuperClass* clone() const {
return new SuperClass(*this);
}
};
class SubClass : public SuperClass{
public:
SuperClass* clone() const final {
return new SubClass(*this);
}
};
int main(void) {
SubClass *sub1 = new SubClass();
SuperClass *super = sub1->clone();
//SubClass *sub2 = super; // 에러 발생.
SubClass *sub2 = dynamic_cast<SubClass*>(super);
return 0;
}
설명
먼저, SuperClass에서 자기 자신을 생성하는 clone() 함수를 가상 함수로 만들었습니다.
class SuperClass {
public:
virtual SuperClass* clone() const {
return new SuperClass(*this);
}
};
이후, SuperClass의 자식클래스인 SubClass에서는 자기 자신을 생성하는 clone() 함수를 오버라이딩 합니다. 이때, 가상 함수 clone()의 반환 타입은 SuperClass인 것을 확인할 수 있습니다.
따라서, 자식클래스인 SubClass의 객체를 생성하지만, 리턴할 때는 SuperClass의 객체로 변환되는 것입니다.
class SubClass : public SuperClass{
public:
SuperClass* clone() const final {
return new SubClass(*this);
}
};
이것은 아래와 같은 문제가 발생합니다.
아래는 main 함수에서 SubClass의 객체로 sub1을 생성합니다. 이후에, sub1 객체를 clone() 함수를 이용하여 복사하려 하지만, 바로 SubClass 객체에 대입할 수 없습니다.
그 이유는 SubClass clone() 함수의 반환 타입이 SuperClass이기 때문입니다. 이로 인해서, SuperClass 객체를 생성하여 대입해야 하는 문제가 생긴 것입니다.
int main(void) {
SubClass *sub1 = new SubClass();
SuperClass *super = sub1->clone();
//SubClass *sub2 = super; // 에러 발생.
SubClass *sub2 = dynamic_cast<SubClass*>(super);
return 0;
}
때문에, 결국, super라는 객체는 sub2라는 객체에 대입되기 위해서 dynamic_cast로 캐스팅을 하여 문제를 해결하게 됩니다.
이는 결국 코드가 복잡해지는 문제가 발생합니다.
부모와 자식의 가상 함수 반환타입이 다르다면, 이러한 문제를 해결할 수 있을까요? 잠시 생각해보시고, 바로 아래로 따라와주세요.
|
부모 클래스와 자식 클래스의 가상 함수 반환타입이 다른 경우. |
아래 예제는 위에 예제와 아주 유사하지만, 가상 함수의 반환타입이 다른 경우입니다.
일단, 유의해야할 부분은 아주 예외적인 상황으로 가상 함수의 반환타입이 다를 수 있다는 것입니다.
#include <iostream>
class SuperClass {
public:
virtual SuperClass* clone() const {
return new SuperClass(*this);
}
};
class SubClass : public SuperClass{
public:
SubClass* clone() const final {
return new SubClass(*this);
}
};
int main(void) {
SubClass *sub1 = new SubClass();
SubClass *sub2 = sub1->clone();
return 0;
}
설명
SuperClass 부분은 위와 같으므로 설명은 생략하도록 하겠습니다.
SubClass의 clone() 반환타입이 위의 예제와 달라졌다는 것을 느끼실 수 있으신가요? 이번 포스팅에서 주구장창 이야기했던, 반환타입을 SubClass로 입력했다는 점이 달라졌습니다.
이로 인해서, SubClass의 객체는 자기 자신을 바로 반환할 수 있게 되겠죠. 그럼, main에서 사용하는 방법은 어떻게 달라졌는 지 확인해볼까요.
class SubClass : public SuperClass{
public:
SubClass* clone() const final {
return new SubClass(*this);
}
};
위에서는 SubClass의 객체를 생성한 후, 복사하려면 바로 대입할 수 없었습니다. 왜냐하면, clone() 반환타입이 달랐으니 말입니다.
하지만, 반환타입이 SubClass로 바뀌면서, 아래 예제 *sub2 = sub1->clone() 처럼 간결하게 코드를 만들 수 있게됩니다.
즉, dynamic_cast와 같은 불필요한 캐스팅을 줄일 수 있게 되는 것입니다.
int main(void) {
SubClass *sub1 = new SubClass();
SubClass *sub2 = sub1->clone();
return 0;
}
이상으로 포스팅 내용 정리를 마치겠습니다. 혹시라도 잘못된 내용이 있다면 댓글 남겨주세요.
'프로그래밍 > C || CPP' 카테고리의 다른 글
| c언어 범위 별 변수 차이를 알아보자 (identifier scope) (0) | 2019.07.17 |
|---|---|
| [C/C++] unsigned char 타입에 음수를 넣은 값이 signed short, int로 형변환 하면 이상한 값으로 변하는 이유 (3) | 2019.07.06 |
| C++ virtual, override, final 키워드별 사용하는 이유를 알아보자. (2) | 2019.06.18 |
| [C || C++] Cpplint로 Google C++ Coding Style Guide 체크해보자. (2) | 2019.03.24 |
| [C || CPP] C++ 배열 초기화 하는 방법 (fill_n) (0) | 2018.10.14 |
댓글