본문 바로가기
C++/Effective Modern C++

재정의 함수들은 override로

by COCO1337 2020. 7. 5.

파생 클래스의 가상함수 구현이 부모 클래스의 해당 가상 함수를 재정의 한다. 가상 함수 재정의(overriding)은 파생 클래스 함수를 부모 클래스의 인터페이스를 통해 호출할 수 있게 만드는 메커니즘이다. 재정의를 하기 위해서 필요한 조건들이 있는데, 1. 부모 클래스 함수가 반드시 가상함수여야 하고, 2. base 함수와 derived 함수의 이름이 동일해야 하며, 3. 매개변수 형식들이 동일해야 한다. 또한 4. const성이 동일해야 하며, 5. 반환 형식과 예외 명세가 호환되어야 한다. 그리고 6. 참조 한정사들이 반드시 동일해야 한다.

 

C++11은 파생 클래스 함수가 부모 클래스 버전을 재정의 한다는 의도를 override를 사용해 명시적으로 표현한다.

class Base {
public:
    virtual void mf1();
    virtual void mf2(unsigned int x) ;
    virtual void mf3() &&;
    virtual void mf4() const;
}

class Derived : public Base {
public:
    virtual void mf1() override;
    virtual void mf2(unsigned int x)  override;
    virtual void mf3() &&  override;
    virtual void mf4() const  override;
}

override를 사용하지 않으면 부모 클래스의 함수 재정의를 시도할 때 그렇게 되지 않는 파생 클래스 가항 함수들에 대해 컴파일러가 경고를 하지 않는다.

 

※ 문맥 의존 키워드

오직 특정 문맥에서만 예약어로 작용하는 특성이 있다.

C++11에서 override와 final이 추가되었으며, override는 멤버 함수 선언의 끝에 나올때에만 예약된 의미를 가진다. final은 가상 함수에 적용시켜 파생 클래스에서 그 함수를 재정의 할 수 없게 한다.

 


멤버 함수에 참조 한정사를 붙여야 하는 상황에 대한 예시이다.

class Widget{
public:
    using DataType = std::vector<double>;
    ...
    DataType& data() {return values;}	// std::vector<double>에 직접 접근할 수 있음
    ...
private:
    DataType values;
};

Widget w;
auto vals1 = w.data();		// w.values.를 vals1에 복사(Widget::data()의 반환 형식이 왼값 참조이므로 복사 생성)

Widget makeWidget(); 		// Widget을 생성하는 팩토리 함수
auto vals2 = makeWidget().data();	// Widget 안에 있는 values를 vals2에 복사

auto vals2 = makeWidget().data();에너 Widget::data는 왼값 참조를 돌려주며, vals2는 Widget안의 values들로부터 복사 생성된다. 하지만 Widget이 makeWidget이 돌려준 임시 객체(오른값)이다. 따라서 컴파일러는 data가 왼값 참조를 돌려주게 하기 위해 std::vector의 복사 연산을 수행한다.

이처럼 중의적으로 호출되는 것을 피하기 위해 오른값과 왼값에 대해 개별적으로 중복 적재 하는 방법을 사용한다.

class Widget{
public:
    using DataType = std::vector<double>;
    ...
    DataType& data() & {return values;}
    DataType&& data() && {return std:move(values);}
    ...
private:
    DataType values;
};

auto vals1 = w.data(); 			//Widget::data의 왼값, vals1은 복사 생성
auto vals2 = makeWidget().data(); 	//Widget::data의 오른값, vals2는 이동 생성

Conclusion

- 재정의 함수는 override로 선언하라

- 멤버 함수 참조 한정사를 이용하면 멤버 함수가 호출되는 객체(*this)의 왼값 버전과 오른값 버전을 다른 방식으로 처리할 수 있다.


Reference

Effective Modern C++ 항목 12: 재정의 함수들을 override로 선언하라

반응형

댓글