함수 오버라이딩(function overriding)은 C++에서 가장 널리 사용되는 핵심 기능 중 하나입니다. 함수 오버라이딩이란 기본 클래스(Base Class)에 이미 존재하는 함수를 파생 클래스(Derived Class)에서 다시 정의하는 것을 의미합니다. 이때 두 함수의 시그니처(signature)는 동일하지만, 실제 동작은 서로 다르게 구현됩니다.
그런데 프로그래머가 오버라이딩 과정에서 실수를 저지르는 경우가 종종 있습니다. 예를 들어 함수 시그니처가 기본 클래스와 일치하지 않으면, 의도했던 '오버라이딩'이 아니라 완전히 새로운 별개의 함수로 취급됩니다. 문제는 이런 실수가 컴파일러 입장에서는 오류가 아니기 때문에 개발자가 쉽게 눈치채지 못한다는 점입니다.
바로 이런 상황에서 override 키워드가 유용하게 사용됩니다. C++11부터 도입된 이 키워드를 붙이면, 컴파일러는 해당 함수가 반드시 기본 클래스의 가상 함수(virtual function)를 재정의해야 한다는 사실을 인지하고, 조건이 맞지 않을 경우 명확한 에러를 발생시켜 줍니다.
1. 정상적인 함수 오버라이딩 예제
먼저 시그니처가 일치하는 정상적인 오버라이딩 예제를 살펴보겠습니다.
#include <iostream>
using namespace std;
class BaseClass{
public:
virtual void display() {
cout << "Displaying from Base Class\n";
}
};
class DerivedClass : public BaseClass{
public:
void display() {
cout << "Displaying from Derived Class\n";
}
};
main() {
BaseClass *b_ptr;
b_ptr = new DerivedClass();
b_ptr->display();
}실행 결과
Displaying from Derived Class
기본 클래스 포인터가 파생 클래스 객체를 가리키고 있고, display() 함수는 가상 함수로 선언되어 있기 때문에 다형성(polymorphism)에 따라 파생 클래스의 함수가 호출됩니다. 시그니처가 동일하므로 프로그램은 아무 문제 없이 정상 동작합니다.
2. override 키워드로 오류 잡아내기
이번에는 시그니처가 일치하지 않는 경우를 살펴보겠습니다. 파생 클래스의 display() 함수에 매개변수 int x를 추가하고, 여기에 override 키워드를 붙였습니다.
#include <iostream>
using namespace std;
class BaseClass{
public:
virtual void display() {
cout << "Displaying from Base Class\n";
}
};
class DerivedClass : public BaseClass{
public:
void display(int x) override{
cout << "Displaying from Derived Class\n";
}
};
main() {
BaseClass *b_ptr;
b_ptr = new DerivedClass();
b_ptr->display();
}컴파일 결과
[Error] 'void DerivedClass::display(int)' marked override, but does not override
매개변수가 추가되어 시그니처가 달라졌기 때문에, 이 함수는 더 이상 기본 클래스의 display()를 재정의하는 것이 아닙니다. 만약 override 키워드가 없었다면 이 코드는 그냥 새로운 함수로 간주되어 조용히 컴파일되었을 것입니다. 하지만 override 키워드 덕분에 컴파일러가 "override로 표시되었지만 실제로는 아무것도 재정의하지 않는다"는 명확한 에러 메시지를 출력해 줍니다.
정리
- 함수 오버라이딩은 기본 클래스의 가상 함수를 파생 클래스에서 같은 시그니처로 다시 정의하는 것입니다.
- 시그니처가 조금이라도 다르면 의도와 달리 새로운 함수로 취급되어, 런타임에 다형성이 깨질 수 있습니다.
- C++11의
override키워드를 사용하면 이런 실수를 컴파일 타임에 즉시 발견할 수 있습니다. - 따라서 가상 함수를 재정의할 때는 항상
override키워드를 붙이는 것이 안전하고 권장되는 코딩 스타일입니다.