SW 그리고 아빠 엔지니어링 중...

아는 만큼 보이고, 경험해봐야 알 수 있고, 자꾸 써야 내 것이 된다.

L C++

[C++] virtual 함수 상속 시에는 override 키워드를 꼭 쓰자

보리남편 김 주부 2022. 12. 11. 04:38
특정 플랫폼에 이슈가 있어서 담당 플랫폼에 이슈가 있는지 코드로 확인해 보다 발견한 override 키워드, 이슈 내용 확인을 위해 무심코 지나갔지만, 함수 옆에 일일이 달려있는 override 키워드를 보며 나처럼 '누가 이리 번거롭게 키워드를 달았을까?'라는 생각을 하는 사람이 있다면 나와 함께 override에 대해 다시 공부하자.
그러나~~ 만약 본인이 virtual 함수를 모른다면, override와 overload가 헷갈린다면
도움이 안 되니 창을 닫거나 과감히 마우스 back 버튼을 눌러주세요.

 

아래 프로그램은 override 키워드를 알아가기 위한 코드로

main() 함수를 보면 Base Class와 Base Class를 상속받은 Override class에 함수를 실행하는 프로그램이다.

int main(){
    Base b, *pB;
    Override o;

    pB = &b;
    pB->virtual_func(1); //Base 의 virtual 함수 실행
    pB = &o;             //Base를 상속받은 Override 의 주소를 대입
    pB->virtual_func(1); //결과는 ??
    pB->func(1);         //결과는 ??

    return 0;
}

 

간단한 코드이니 아래 코드를 보고 결과를 예측해봅시다.

Base : int 1 virtual_func
????: int 1 virtual_func
???? : int 1 func

 

//override.h
#pragma once

class Base {
    public:
    Base();
    virtual int virtual_func(int nVal);
    int func(int nVal);
};

class Override : public Base {
    public:
    Override();
    virtual int virtual_func(float nVal);

    int func(int nVal);
};
//override.cpp
#include <iostream>
#include "override.h"

Base::Base(){

}

Override::Override(){

}

int Base::virtual_func(int nVal){
    std::cout << "Base : int " << nVal << " " << __func__ << "\n";
    return 0;
}

int Base::func(int nVal){
    std::cout << "Base : int " << nVal << " " << __func__ << "\n";
    return 0;
}

int Override::virtual_func(float nVal){
    std::cout << "Override : float " << nVal << " " << __func__ << "\n";
    return 0;
}

int Override::virtual_func(int nVal){
    std::cout << "Override : int " << nVal << " " << __func__ << "\n";
    return 0;
}

int Override::func(int nVal){
    std::cout << "Override : int " << nVal << " " << __func__ << "\n";
    return 0;
}

int main(){
    Base b, *pB;
    Override o;

    pB = &b;
    pB->virtual_func(1);
    pB = &o;
    pB->virtual_func(1);
    pB->func(1);

    return 0;
}

 

우선 실행시켜 봅시다. (C++ 개발환경이 설치되어 있지 않다면 더보기 안에 개발 환경 설정하기 참조할 것)

 

결과
더보기

Base : int 1 virtual_func
Base : int 1 virtual_func
Base : int 1 func

: 생각했던 대로 동작을 했나요?

분명히 override Class에 virtual 함수를 호출했는데 의도치 않게 Base의 virtual 함수가 실행됩니다. 뭐가 문제였을까요?

 

Override Class에 virtual_func() 함수를 다시 봅시다.

부모 Class의 virtual_func()의 인자 타입과 다르군요. 실제로 이런 일 없을 것 같죠?

//override.h
#pragma once

class Base {
    public:
    Base();
    virtual int virtual_func(int nVal);
    int func(int nVal);
};

class Override : public Base {
    public:
    Override();
    virtual int virtual_func(float nVal); //인자 type이 달라서 Base 의 virtual_func 와 전혀 다른 함수임

    int func(int nVal);
};
누가 바꿔놨어?
A 군이 구현한 Base Class에 virtual_func() 함수 인자 값이 int 범위를 넘어간다는 고객의 요청사항에 맞춰 double 타입으로 변경을 하였습니다. B군은 어느 날부터 Override Class에 virtual_func() 함수가 호출되지 않아서 디버깅을 해보다 발견을 했다. "누가 부모 클래스만 수정을 했어?"

:부모/자식 Class에 virtual_func() 함수명은 같지만 개발자 의도와 다르게 전혀 다른 함수이다. 즉 override 가 되지 않은 별개의 virtural 함수이다.

 

부모 Class의 virtual 함수와 같지만 Virtual 키워드가 빠져 있다면 결과는?
//override.h
#pragma once

class Base {
    public:
    Base();
    virtual int virtual_func(int nVal);
    int func(int nVal);
};

class Override : public Base {
    public:
    Override();
    int virtual_func(int nVal); //virtual 키워드를 뺀다면?
    int func(int nVal);
};
결과
더보기

Base : int 1 virtual_func
Override : int 1 virtual_func
Base : int 1 func

: 상속받은 자식 Class가 부모 Class에 virtual 함수와 같다면 자식 Class의 함수가 호출이 된다.

하지만 virtual 키워드가 없다면 virtual 함수와 자체 구현 함수와 구분이 되지 않기에 virtual 함수를 넣는 것만으로도 가독성이 올라간다.

 

자식 클래스에 override 키워드를 넣는다면 결과는?
//override.h
#pragma once

class Base {
    public:
    Base();
    virtual int virtual_func(int nVal);
    int func(int nVal);
};

class Override : public Base {
    public:
    Override();
    int virtual_func(int nVal) override; //override 키워드를 추가한다면?
    int func(int nVal);
};
결과
더보기

Base : int 1 virtual_func
Override : int 1 virtual_func
Base : int 1 func

: 뭐지?????  override 키워드를 추가했다고 결과는 달라지지 않는다.

 

override 키워드의 필요성은 사용할 때 발생하는 것이 아니고 개발할 때 발생을 한다.

 

상속받은 Class에서 인자 타입이 다른 함수에 override 키워드를 넣는다면 결과는?
//override.h
#pragma once

class Base {
    public:
    Base();
    virtual int virtual_func(int nVal);
    int func(int nVal);
};

class Override : public Base {
    public:
    Override();
    virtual int virtual_func(float nVal) override ; //인자 type이 다르지만 override 키워드가 붙는다면?
    int func(int nVal);
};
결과
더보기

In file included from override.cpp:2:0:
override.h:14:17: error: 'virtual int Override::virtual_func(float)' marked 'override', but does not override
     virtual int virtual_func(float nVal) override;
                 ^~~~~~~~~~~~

: 컴파일러에서 에러가 발생한다. 명시적으로 override 함수라고 선언을 하면 컴파일러가 부모 Class에서 override 된 함수가 맞는지 확인을 하고 틀리면 에러를 발생시켜 사전에 문제점을 발견할 수 있게 된다.

 

virtual 함수가 아닌데 상속받은 Class에서 override 키워드를 추가한 결과는?
//override.h
#pragma once

class Base {
    public:
    Base();
    virtual int virtual_func(int nVal);
    int func(int nVal); //virtual 함수가 아님
};

class Override : public Base {
    public:
    Override();
    virtual int virtual_func(float nVal) ;
    int func(int nVal) override; //virtual 함수가 아닌데 override 추가 
};
결과
더보기

In file included from override.cpp:2:0:
override.h:17:9: error: 'int Override::func(int)' marked 'override', but does not override
     int func(int nVal) override;
          ^~~~

: 컴파일러에서 에러가 발생한다. override 가 되는 함수는 virtual 함수인데, 동일한 함수명이더라도 virtual 함수가 아닌 경우는 컴파일러가 에러를 발생시켜 개발자에게 문제점을 점검할 수 있게 해 준다.

 

 

virual 함수를 의도에 맞게 사용하는지 컴파일러에서 체크해 걸러주니 휴먼에러로 발생하는 오동작률을 사전에 낮출 수 있기에 이제부터라도 override 키워드를 추가하는 번거로움은 기쁘게 타이핑하자.

728x90
728x90

'L C++' 카테고리의 다른 글

직접선언! 낄낄빠빠하자.  (0) 2024.09.09
40년간의 C++ 표준 변화 요약  (2) 2023.08.08