C++이야기 스물네번째: Override할 가상함수의 Prototype은 확인 또 확인!

Posted at 2008. 10. 31. 00:46 // in S/W개발/C++ 이야기 // by 김윤수


이번에는 C++로 프로그래밍하다가 저지르기 쉬운 실수에 대해 한 번 말씀드리겠습니다. 가상함수 관련된 실수입니다. 여러 말보다는 한 줄 코딩이 낫다고 우선 코드부터 보시죠.

#include <iostream>
#include <string>

using namespace std;

class Base
{
public:
    virtual void hello(const string& name)
    {
        cout << "Don't call Base::hello(" << name << ")" << endl;
    }
};

class Derived : public Base
{
public:
    virtual void hello(const string name)
    {
        cout << "Hello, " << name << "!" << endl;
    }
};

int
main()
{
    Base* p = new Derived();

    p->hello("KKK");
}

코드 잘 보셨죠? 자~ 그럼 퀴즈 들어갑니다. 위 코드를 컴파일해서 실행하면 어떤 결과가 나올까요? 엇! 이런 것도 퀴즈라고 날 어떻게 보고 하시면서 눈을 부라리는 분이 계시군요. 오늘 퀴즈는 그런분들을 위해 내는 것이니 절 보고 눈 부라리시지 마시고, 코드를 다시 한 번 보시기 바랍니다. 어떠세요? 이런 결과가 나올 것이라고 예상하셨나요?

Hello, KKK!

앗! 그런데 어떡하죠? 제 컴퓨터에서는 이렇게 나오네요.

Don't call Base::hello(KKK)

이게 무슨 조화죠? p 가 Base* 형이긴 하지만 Derived()를 생성했으니까 당연히 "Hello, KKK!"라고 찍혀야 하는 거 아닌가? 정 저를 못 믿으시겠다면 직접 한 번 컴파일하시고 실행해 보세요~ 어때요? 제 말이 맞죠? ㅋㅋㅋ

다시 퀴즈 들어갑니다. 그렇다면 왜 이런 결과가 발생하는 걸까요?



















십! 삐~

시간 초과되셨습니다.

다시 한 번 코드를 자세히 들여다 보겠습니다.

class Base
{
public:
    virtual void hello(const string& name) { ...... }
};

class Derived : public Base
{
public:
    virtual void hello(const string name) { ...... }
};

빨갛게 표시한 부분의 차이점이 보이시나요? 그렇습니다. 단지 '&'가 빠져있을 뿐입니다. C++ 컴파일러가 가상함수의 override 여부를 판단할 때는 함수 인자가 정확히 일치될 경우에만 override 된 것으로 판단합니다. 위 경우에는 hello member function이 override된 것이 아니라 overload된 경우가 될 것입니다. 즉, 별개의 멤버 함수인 것이죠. 따라서 Base* 형인 p에 대해  hello()를 호출하면 원래의 Base::hello() 가 호출되어 버리는 것입니다.

이 예는 비교적 간단한 예라서 비교적 찾기 쉽지 실제 프로그램이 복잡해지면 이런 에러는 정말 찾기가 힘듭니다. 그러니 코드 리뷰하면서 미리 미리 가상함수 상속받아서 override하면서 함수 Prototype을 일치시켰는지 꼭 확인에 확인해 보시기 바랍니다. 저도 얼마전에 이 문제 때문에 한참을 헤맸답니다.ㅠ.ㅠ

이제 마무리 들어갑니다. 가상함수를 쓰실 때는 다음 주의사항을 꼭 명심하시기 바랍니다.

"Override할 가상함수의 Prototype은 확인 또 확인하자"