KoreanFoodie's Study
Effective Modern C++ | 항목 12 : 재정의 함수들을 override 로 선언하라 본문
Tutorials/C++ : Advanced
Effective Modern C++ | 항목 12 : 재정의 함수들을 override 로 선언하라
GoldGiver 2022. 10. 26. 09:53
C++ 프로그래머의 필독서이자 바이블인, 스콧 마이어스의 Modern Effective C++ 를 읽고 기억할 내용을 요약하고 있습니다. 꼭 읽어보시길 추천드립니다!
항목 12 : 재정의 함수들을 override 로 선언하라
핵심 :
1. 재정의 함수는 override 로 선언하라.
2. 멤버 함수 참조 한정사(reference qualifier)를 이용하면 멤버 함수가 호출되는 객체(*this)의 왼값 버전과 오른값 버전을 다른 방식으로 처리할 수 있다.
기반 클래스에서 정의된 가상 함수가 파생 클래스에서 재정의되기 위해서는 다음과 같은 조건들이 만족 되어야 한다.
- 기반 클래스 함수가 가상함수 이어야 함
- 기반 함수와 파생 함수의 이름이 동일해야 함(소멸자 제외)
- 기반 함수와 파생 함수의 매개변수 형식들이 동일해야 함
- 기반 함수와 파생 함수의 const 성이 동일해야 함
- 기반 함수와 파생 함수의 반환 형식과 예외 명세(exception specification) 가 반드시 호환되어야 함
C++11 에는 다음과 같은 조건 하나가 추가되었다.
- 멤버 함수들의 참조 한정사(reference qualifier) 들이 동일해야 함
멤버 함수 참조 한정사는 멤버 함수를 왼값에만 또는 오른값에만 사용할 수 있게 제한하는 기능이다. 비가상 함수에도 멤버 함수 참조 한정사를 적용할 수 있다.
class Widget {
public:
// 이 버전은 *this 가 왼값일 때에만 적용
void doWork() &;
// 이 버전은 *this 가 오른값일 때에만 적용
void doWork() &&;
};
...
// 팩터리 함수 (오른값 반환)
Widget makeWidget();
// 왼값을 돌려줌
Widget w;
...
// Widget::doWork() & 호출 (왼값 버전)
w.doWork();
// Widget::doWork() && 호출 (오른값 버전)
makeWidget().doWork();
기반 클래스의 가상 함수에 참조 한정사가 있으면, 해당 함수를 오버라이딩 하기 위해서는 파생 클래스의 가상 함수에도 정확히 같은 참조 한정사가 붙어 있어야 한다. 그렇지 않으면 오버라이딩이 아니라 숨김(hide) 이 일어난다. 아래 코드를 보며 문제점을 다시 확인해 보자.
class Base {
public:
virtual void mf1() const;
virtual void mf2(int x);
virtual void mf3() &;
void mf4() const;
};
class Derived {
public:
virtual void mf1();
virtual void mf2(unsigned int x);
virtual void mf3() &&;
void mf4() const;
};
- mf1 : const 가 없음
- mf2 : 매개변수 타입이 다름
- mf3 : 참조 한정사가 다름
- mf4 : 기반 클래스에서 가상 함수로 선언되지 않음
위의 함수들의 끝에 override 를 붙여주면, 컴파일러가 재정의 관련 문제점들을 지적해 준다. 위 코드를 수정하면 다음과 같다.
class Base {
public:
virtual void mf1() const;
virtual void mf2(int x);
virtual void mf3() &;
virtual void mf4() const;
};
class Derived {
public:
virtual void mf1() const override;
virtual void mf2(int x) override;
virtual void mf3() & override;
void mf4() const override; // virtual 은 붙여도, 안 붙여도 됨
};
override 는 키워드로, 함수 마지막에 붙었을 때만 예약된 의미를 가지므로, void override(); 같은 코드가 나와도 너무 당황하지는 말자.
이제 참조 한정사를 사용하는 실 사용 예시를 보자.
class Widget {
public:
using DataType = std::vector<double>;
DataType& data() & { return values; }
DataType&& data() && { return values; }
private:
DataType values;
};
// 팩터리 함수
Widget makeWidget();
...
Widget w;
// Widget::data 의 왼값 버전 호출
// vals1 은 복사 생성됨
auto vals1 = w.data();
// Widget::data 의 오른값 버전 호출
// vals2 은 이동 생성됨
auto vals2 = makeWidget().data();
만약 위의 코드에서, data( ) 함수에 참조 한정사가 붙지 않고 참조 타입만 리턴하는 경우, val2 의 경우 이동 생성이 아니라 복사 생성이 일어난다. 왜냐하면 data( ) 는 왼값 참조를 돌려주기 때문이다! 하지만 오른값을 전달할 경우, 이동 생성이 가능하게 되는 구현을 원하므로, 참조 한정사를 붙임으로써 이러한 문제를 해결했다.
'Tutorials > C++ : Advanced' 카테고리의 다른 글
Effective Modern C++ | 항목 14 : 예외를 방출하지 않을 함수는 noexcept 로 선언하라 (0) | 2022.10.26 |
---|---|
Effective Modern C++ | 항목 13 : iterator 보다 const_iterator 를 선호하라 (0) | 2022.10.26 |
Effective Modern C++ | 항목 11 : 정의되지 않은 비공개 함수보다 삭제된 함수를 선호하라 (0) | 2022.10.26 |
Effective Modern C++ | 항목 10 : 범위 없는 enum 보다 범위 있는 enum 을 선호하라 (0) | 2022.10.26 |
Effective Modern C++ | 항목 9 : typedef 보다 별칭 선언을 선호하라 (0) | 2022.10.26 |
Comments