파생 클래스의 가상 함수 구현은 대체로 기반 클래스의 가상 함수 부분을 재정의 하게 된다.
파생 클래스에서 함수의 재정의가 일어나려면 다음과 같은 필수 조건들을 만족해야 한다.
◾ 기반 클래스의 함수가 반드시 가상 함수이어야 한다.
◾ 기반 함수의 파생 함수의 이름이 반드시 동일해야 한다.(소멸자 제외)
◾ 기반 함수의 파생 함수의 매개변수 타입들이 반드시 동일해야 한다.
◾ 기반 함수와 파생 함수의 상수성(const)이 반드시 동일해야 한다.
◾ 기반 함수와 파생 함수의 반환 타입과 예외 명세가 반드시 호환되어야 한다.
위의 제약들은 C++98에도 있던것들이고 C++11에서 참조 한정사들이 동일해야 한다는 제약이 하나 더 추가됐다.
참조 한정사는 멤버 함수를 왼값 또는 오른값에만 사용할 수 있도록 제약시키는 것이다.
class Widget {
public:
void doWork() &; // *this가 왼값일때만 호출
void doWork() &&; // *this가 오른값일때만 호출
...
};
기반 함수가 상수성을 가지고 있다면 파생 함수에서도 상수성을 가져야하는 것처럼 참조 한정사 역시 동일하게 유지시켜주어야 한다. 만약 참조 한정사가 서로 다르다면 재정의가 일어나지 않는다.
class Base {
public:
virtual void mf1() const;
virtual void mf2(int x);
virtual void mf3() &;
void mf4() const;
};
class Derived: public Base {
public:
virtual void mf1(); // 상수성 유지 x
virtual void mf2(unsigned int x); // 매개변수 타입이 다름
virtual oid mf3() &&; // 참조 한정사가 다름
void mf4() const; // 가상 함수가 아님
};
위의 4가지 경우 모두 재정의가 이루어지지 않은 경우들이다. 무서운점은 컴파일러가 경고를 띄워주지 않는다는 점이다. 명백한 실수를 저질렀음에도 모르고 넘어갈 수 있는 것이다.
그래서 파생 클래스에서 함수를 재정의 할 때, 기반 클래스의 함수를 재정의 한다고 명시하기 위해 override 식별자를 사용한다.
class Derived: public Base {
public:
virtual void mf1() override;
virtual void mf2(unsigned int x);
virtual void mf3() && override;
virtual void mf4() override;
};
이제 이 코드는 컴파일되지 않는다. 재정의 명시를 했지만 재정의가 이루어진게 하나도 없기 때문이다.
마지막으로 참조 한정사가 붙은 함수가 오버로딩 되었다면 오버로딩된 모든 함수들에도 참조 한정사를 지정해주어야 한다. 그렇지 않으면 중의적인 호출이 되기 때문에 의도하지 않은 호출이 이뤄질 수 있다.
◾ 재정의 함수는 override로 선언하라.
◾ 멤버 함수 참조 한정사를 이용하면 멤버 함수가 호출되는 객체(*this)의 왼값 버전과 오른값 버전을 다른 방식으로 처리할 수 있다.
'도서 > Effective Modern C++' 카테고리의 다른 글
[3장] 항목 14 : 예외를 방출하지 않을 함수는 noexcept로 선언하라 (0) | 2023.01.17 |
---|---|
[3장] 항목 13 : iterator보다 const_iterator를 선호하라 (0) | 2023.01.17 |
[3장] 항목 11 : 정의되지 않은 비공개 함수보다 삭제된 함수를 선호하라 (0) | 2022.12.29 |
[3장] 항목 10 : 범위 없는 enum보다 범위 있는 enum을 선호하라 (0) | 2022.12.29 |
[3장] 항목 9 : typedef보다 별칭 선언을 선호하라 (0) | 2022.12.28 |