final 지정자
final 지정자 (final specifier)
가상 함수(virtual function)가 파생 클래스에서 더는 오버라이드될 수 없게 막거나, 클래스가 더는 상속될 수 없게 막는 final 지정자를 알아볼게요. 상속 설계에서 '여기까지'를 명시적으로 못 박고 싶을 때 쓰는 키워드입니다.
출처: cppreference
본문
문법
멤버 함수에 적용할 때 final 식별자는 클래스 정의 안의 멤버 함수 선언 또는 멤버 함수 정의 문법에서 declarator 바로 뒤에 나타나요.
클래스(구조체 struct와 공용체 union 포함)에 적용할 때 final 식별자는 클래스 정의의 맨 앞, 클래스 이름 바로 뒤에 나타나며, 클래스 선언(class declaration)에는 쓸 수 없어요.
declarator virt-specifier-seq (optional) pure-specifier (optional) (1)
declarator virt-specifier-seq (optional) function-body (2)
class-key attr (optional) class-head-name class-virt-specifier (optional) base-clause (optional) (3)
- (1) 멤버 함수 선언에서
final은 declarator 바로 뒤, pure-specifier(쓰는 경우) 앞의 virt-specifier-seq에 나타날 수 있어요. - (2) 클래스 정의 안의 멤버 함수 정의에서
final은 declarator 바로 뒤, function-body 바로 앞의 virt-specifier-seq에 나타날 수 있어요. - (3) 클래스 정의에서
final은 클래스 이름 바로 뒤, base-clause가 시작되는 콜론 바로 앞의 class-virt-specifier로 나타날 수 있어요.
(1), (2)의 경우 virt-specifier-seq는 쓰면 override나 final, 또는 final override나 override final 중 하나예요. (3)의 경우 class-virt-specifier의 허용 값은 쓰면 final뿐입니다.
설명
가상 함수 선언이나 정의에 final을 쓰면, 이 함수가 가상임을 보장하고 파생 클래스에서 오버라이드될 수 없음을 지정해요. 그렇지 않으면 프로그램은 ill-formed이 되며(즉 컴파일 타임 오류가 생성되고) 컴파일되지 않아요.
클래스 정의에 final을 쓰면 이 클래스가 다른 클래스 정의의 base-specifier-list에 나타날 수 없다는 것, 다시 말해 상속될 수 없다는 것을 지정해요. 그렇지 않으면 ill-formed이 됩니다. (컴파일 타임 오류가 생성돼요.) final은 공용체 정의에도 쓸 수 있는데, 공용체는 어차피 상속될 수 없으므로 아무 효과가 없어요(std::is_final의 결과에 영향을 주는 것 외에는, C++14부터).
final은 멤버 함수 선언이나 클래스 헤드(class head)에 쓰일 때 특별한 의미를 갖는 식별자예요. 다른 맥락에서는 예약어가 아니며, 객체나 함수의 이름으로 쓸 수 있습니다.
참고 사항
다음 토큰들의 연속에서:
class,struct,union중 하나- 한정될 수 있는 식별자(possibly qualified identifier)
final:와{중 하나
세 번째 토큰 final은 항상 식별자가 아닌 지정자(specifier)로 간주돼요.
struct A;
struct A final {}; // OK, definition of struct A,
// not value-initialization of variable final
struct X
{
struct C { constexpr operator int() { return 5; } };
struct B final : C{}; // OK, definition of nested class B,
// not declaration of a bit-field member final
};
// Abnormal final usage.
struct final final // OK, definition of a struct named “final” from which
{ // you cannot inherit
};
// struct final final {}; // Error: redefinition of “struct final”, NOT a
// definition of a variable “final” using an elaborated
// type specifier “struct final” followed by an
// aggregate initialization
// struct override : final {}; // Error: cannot derive from final base type;
// “override” in given context is a normal name
void foo()
{
[[maybe_unused]]
final final; // OK, declaration of a variable named “final” of type
// “struct final”
}
struct final final; // OK, declaration of a variable named “final” of type
// “struct final” using an elaborated type specifier
int main()
{
}
키워드
final
예제
final이 가상 함수와 파생에 각각 어떻게 적용되는지, 어떤 자리에서 오류가 나는지 확인해 볼게요.
struct Base
{
virtual void foo();
};
struct A : Base
{
void foo() final; // Base::foo is overridden and A::foo is the final override
void bar() final; // Error: bar cannot be final as it is non-virtual
};
struct B final : A // struct B is final
{
void foo() override; // Error: foo cannot be overridden as it is final in A
};
struct C : B {}; // Error: B is final
가능한 출력:
main.cpp:9:10: error: 'void A::bar()' marked 'final', but is not virtual
9 | void bar() final; // Error: bar cannot be final as it is non-virtual
| ^~~
main.cpp:14:10: error: virtual function 'virtual void B::foo()' overriding final function
14 | void foo() override; // Error: foo cannot be overridden as it is final in A
| ^~~
main.cpp:8:10: note: overridden function is 'virtual void A::foo()'
8 | void foo() final; // Base::foo is overridden and A::foo is the final override
| ^~~
main.cpp:17:8: error: cannot derive from 'final' base 'B' in derived type 'C'
17 | struct C : B // Error: B is final
|
더 알아보기 (Learn more)
- override 지정자 — 메서드가 다른 메서드를 오버라이드함을 명시적으로 선언 (C++11부터)
- 가상 함수 (virtual functions) — 가상 함수와 오버라이드 규칙