C++
상속을 다루며 많이 보았던 키워드는 virtual
키워드일 것이다. virtual
에 비해 많이 보지 못했던 키워드는 override
와 final
키워드일 것이다.
override
와 final
키워드는 C++11
이후에 등장하는 키워드라서 그렇다. 고로 오래된 C++
책으로 공부를 했다면 virtual
키워드만 보았을 가능성이 높다. virtual
, override
, final
은 모두 상속 관련하여 오버라이딩(overriding)
할 때 사용하는 키워드
들이다. 조금씩 용도는 다르다.
override
와 final
키워드가 새롭게 추가된 이유를 한번 알아보고, 각 키워드
가 무엇을 뜻하는지 살펴보자.
virtual
키워드는 가상함수
를 뜻한다. 가상함수
에는 함수의 몸체를 정의하지 않는 순수 가상함수(추상화)
와 정의를 하는 일반 가상함수
로 나눌 수 있다.
보통 virtual
키워드를 붙이면, 해당 클래스를 상속받는 하위 클래스에서 virtual
키워드의 함수를 재정의(오버라이딩)
한다는 뜻이다. 그리고 virtual
키워드는 가상함수
의 처음 부붙에 붙인다. 즉, 부모 클래스에 붙인다고 생각하면 된다.
가상함수
는 주로 실행 시간(Runtime)
에 함수의 다형성(Polymorphism)
을 구현하는데 사용한다.
#include <iostream>
using namespace std;
class BaseClass
{
public:
virtual void ShowMe()
{
cout << "base class" << endl;
}
virtual ~BaseClass(){};
};
class SubClass : public BaseClass
{
public:
void ShowMe()
{
cout << "sub class" << endl;
}
};
int main()
{
BaseClass *bc;
SubClass sc;
bc = ≻
bc->ShowMe();
return 0;
}
가상함수
선언에는 몇가지 규칙이 존재한다.
공개(public)
섹션에 선언한다.가상함수
는 정적(static)
일 수 없으며, 다른 클래스의 친구(friend)
함수가 될 수 없다.가상함수
는 실행 시간 다형성
을 얻기 위해 기본 클래스의 포인터
또는 참조
를 통해 접근(access)
해야 한다.가상함수
의 프로토타입(반환형과 매개변수)은 기본 클래스와 파생 클래스에서 동일해야 한다.가상 소멸자
를 가질 수 있지만 가상 생성자를 가질 수는 없다.override
키워드는 C++11
이후로 나온 키워드이다. 이 키워드로 가상함수
를 가르키기 위한 키워드라고 할 수 있다. 다만, override
와 virtual
이 다른 점은 키워드를 붙이는 위치라고 할 수 있다. virtual
은 부모 클래스 가상함수
의 시작 부분에 붙였다면, override
는 하위 클래스 가상함수
에 붙인다.
override
키워드가 붙은 함수라면, 이 가상함수는 부모로부터 상속받아 오버라이딩한 함수다 라는 것을 뜻한다.
class SubClass : public BaseClass
{
public:
void ShowMe() override
{
cout << "sub class" << endl;
}
};
만약, 아래처럼 override
키워드가 붙어있는데 부모와 형식(반환형, 매개변수)
이 다르다면 오류를 발생시킨다.
class SubClass : public BaseClass
{
public:
void ShowMe(int a) override
{
cout << "sub class" << endl;
}
};
final
키워드도 C++11
이후로 나온 키워드이다. 이 키워드도 마찬가지로 가상함수
를 가르킨다.
final
은 가상함수
의 마지막을 가르키는 키워드라고 생각하면 된다. 즉, 계속해서 오버라이딩
을 진행하다가 마지막 하위 클래스에서의 가상함수
에는 virtual
, override
가 아닌 final
을 붙인다는 것이다.
final
키워드가 붙었다면, 이 가상함수를 더 이상 오버라이딩하지 않겠다 라는 뜻이 된다.
#include <iostream>
using namespace std;
class BaseClass
{
public:
virtual void ShowMe()
{
cout << "base class" << endl;
}
virtual ~BaseClass(){};
};
class SubClass : public BaseClass
{
public:
void ShowMe() override
{
cout << "sub class" << endl;
}
};
class ChildClass : public SubClass
{
public:
void ShowMe() final
{
cout << "child class" << endl;
}
};
class SubChildClass : public ChildClass
{
public:
// 상속받는 클래스의 가상함수에 final 키워드가 붙어있으므로 에러가 발생한다.
void ShowMe() override
{
cout << "sub child class" << endl;
}
};
int main()
{
BaseClass *bc;
SubChildClass scc;
bc = &scc;
bc->ShowMe();
return 0;
}
간단한 세 가지 키워드로 예제를 살펴보자.
class BaseClass {
// 가상함수의 첫 시작은 virtual 키워드이다. 순수 가상함수로 만듦.
virtual void ShowMe() = 0;
};
class SubClass : public BaseClass {
// 상속받은 가상함수에는 override 키워드를 붙인다.
void ShowMe() override {};
};
class ChildClass : public SubClass {
// 가상함수의 마지막에는 final 키워드를 붙인다.
void ShowMe() final {};
};
먼저, BaseClass
에서 ShowMe()
라는 순수 가상함수
를 만들었다. 이 때, 가상함수
를 하위에서 오버라이딩
할 것이므로 virtual
키워드를 붙였다.
이후, SubClass
에서 BaseClass
를 상속받는다. ShowMe()
가상함수
는 오버라이딩
할 것이므로, override
키워드를 붙였다. 참고로 virtual
키워드를 붙여도 차이점은 없다.
class SubClass : public BaseClass {
// 상속 받은 가상함수에는 override 키워드를 붙인다.
// virtual 키워드를 붙여도 상관없다.
virtual void ShowMe() override {};
};
다만, override
를 붙여주면 이 가상함수
가 처음 선언될 경우에 에러를 발생시킨다. 이로인해 개발시 도움이 된다.
만약 virtual
키워드만 사용한다면 아래와 같은 상황에 직면할 수도 있다.
class BaseClass {
virtual void ShowMe() {};
};
class SubClass : public BaseClass {
virtual void ShowMe(int val) {};
};
위의 ShowMe()
가상함수
들은 서로 다른 가상함수
이다. 왜냐하면 형식이 다르기 때문이다. 그래서 이러한 경우를 예방
하기 위해서 override
키워드를 사용한다. 그러면 빌드할 때, 에러
가 발생하여 잘못되었음을 인지
할 수 있다.
마지막으로 ChildClass
에서는 SubClass
를 상속받는다. ChildClass
에서 ShowMe()
가상함수
에 final
을 붙였으므로 해당 가상함수
는 더 이상 오버라이딩
하지 않겠다라는 의미를 부여한다. 이때에도 마찬가지로 virtual
또는 override
키워드를 붙여도 상관 없다.
class ChildClass : public SubClass {
// virtual 또는 override 또는 virtual, override 모두 붙여도 상관 없다.
virtual void ShowMe() override final {};
};
간단히 말해서 SubClass
를 상속받는 하위 클래스에서 ShowMe()
가상함수
를 더 이상 오버라이딩
할 수 없게 만들겠다는 뜻이다.
만약, final
키워드가 붙은 가상함수
를 오버라이딩
하려고 한다면 빌드 에러
가 발생하여 사전에 잘못된 부분을 고칠 수 있는 이점이 있다.