디자인 관점에서 사물을 변하지 않는 것으로 표시 할 수있는 경우가 종종 있습니다. 같은 방식으로 const
컴파일러 보호 기능을 제공하고 상태가 변경되지 않아야 함 final
을 나타내며, 상속 계층 구조에서 동작이 더 이상 변경되지 않아야 함을 나타내는 데 사용할 수 있습니다.
예
차량이 플레이어를 한 위치에서 다른 위치로 이동시키는 비디오 게임을 고려하십시오. 모든 차량은 출발 전에 유효한 위치로 여행하고 있는지 확인해야합니다 (예 : 위치의 기지가 파괴되지 않았는지 확인). 비가 상 인터페이스 관용구 (NVI)를 사용하여 시작하여 차량과 상관없이이 점검을 수행 할 수 있습니다.
class Vehicle
{
public:
virtual ~Vehicle {}
bool transport(const Location& location)
{
// Mandatory check performed for all vehicle types. We could potentially
// throw or assert here instead of returning true/false depending on the
// exceptional level of the behavior (whether it is a truly exceptional
// control flow resulting from external input errors or whether it's
// simply a bug for the assert approach).
if (valid_location(location))
return travel_to(location);
// If the location is not valid, no vehicle type can go there.
return false;
}
private:
// Overridden by vehicle types. Note that private access here
// does not prevent derived, nonfriends from being able to override
// this function.
virtual bool travel_to(const Location& location) = 0;
};
이제 우리는 게임에 비행 차량이 있고 모든 비행 차량이 필요로하는 공통점은 이륙 전에 격납고 내부의 안전 점검을 거쳐야한다는 것입니다.
여기서 우리는 final
모든 비행 차량이 그러한 검사를 통과하고 비행 차량의 이러한 설계 요구 사항을 전달하도록 보장하는 데 사용할 수 있습니다 .
class FlyingVehicle: public Vehicle
{
private:
bool travel_to(const Location& location) final
{
// Mandatory check performed for all flying vehicle types.
if (safety_inspection())
return fly_to(location);
// If the safety inspection fails for a flying vehicle,
// it will not be allowed to fly to the location.
return false;
}
// Overridden by flying vehicle types.
virtual void safety_inspection() const = 0;
virtual void fly_to(const Location& location) = 0;
};
final
이러한 방식으로 사용함으로써 , 우리는 가상 계층이 아닌 인터페이스 관용구의 유연성을 효과적으로 확장하여 상속 계층 (아래의 취약한 기본 클래스 문제에 대응하는)에서도 가상 함수 자체에 균일 한 동작을 제공합니다. 또한, 우리는 존재하는 각각의 모든 비행 차량 구현을 수정하지 않고 모든 비행 차량 유형에 영향을 미치는 중심 변경을 수행하기 위해 흔들림 공간을 스스로 구매합니다.
이것은를 사용하는 하나의 예입니다 final
. 가상 멤버 기능이 더 이상 재정의되는 것이 단순히 의미가없는 상황에 직면하게됩니다. 그렇게하면 취하기 쉬운 설계로 인해 설계 요구 사항이 위반 될 수 있습니다.
final
디자인 / 건축 관점에서 유용한 곳 입니다.
또한 옵티마이 저가 가상 함수 호출을 실용화 할 수있는이 설계 정보를 옵티 마이저에게 제공하므로 동적 디스패치 오버 헤드를 제거하고 호출자와 피 호출자 간의 최적화 장벽을 제거 할 수 있기 때문에 더욱 유용합니다.
질문
의견에서 :
final과 virtual이 동시에 사용되는 이유는 무엇입니까?
계층 구조의 루트에있는 기본 클래스가 함수를 virtual
및 로 선언하는 것은 이치에 맞지 않습니다 final
. 컴파일러와 인간 독자 모두가 불필요한 경우를 피해야 virtual
하는 경우 가 있기 때문에 나에게는 매우 어리석은 것처럼 보입니다 . 그러나 서브 클래스는 다음과 같이 가상 멤버 함수를 상속합니다.
struct Foo
{
virtual ~Foo() {}
virtual void f() = 0;
};
struct Bar: Foo
{
/*implicitly virtual*/ void f() final {...}
};
이 경우 Bar::f
virtual 키워드를 명시 적으로 사용 하는지 여부 Bar::f
는 가상 함수입니다. virtual
키워드는이 경우에 선택된다. 따라서 가상 함수 (가상 함수 에만 사용할 수 있음) 이지만 Bar::f
로 지정 final
하는 것이 좋습니다.final
그리고 어떤 사람들은 문체 적으로 Bar::f
가상 인 것을 명시 적으로 나타내기를 선호 할 수 있습니다 .
struct Bar: Foo
{
virtual void f() final {...}
};
나에게 그것은 종류 모두 사용하는 중복의의 virtual
와 final
이러한 맥락 (마찬가지로 동일한 기능을 위해 지정자를 virtual
하고 override
)하지만,이 경우 스타일의 문제이다. 어떤 사람들은 외부 연결로 함수 선언에 virtual
사용 extern
하는 것과 같이 (여기에는 다른 연결 한정자가없는 경우에도) 유용한 것을 전달할 수 있습니다 .