2025. 5. 4. 16:42ㆍProgramming Language/C++
1. 가상 함수란
부모 클래스에서 정의한 함수가, 자식 클래스에서 재정의(override) 될 수 있도록 하는 함수로 virtual 이라는 키워드를 사용해서 생성한다.
먼저 부모 자식 클래스를 한번 생성해주자.
먼저 부모와 자식에 동일한 함수를 생성했을 때
메인 함수에서 각각의 객체를 생성해서 각각의 func를 호출해보면
각각의 함수를 호출하는 것을 알 수 있는데 이번엔 업케스팅을 한번 해보고 나서 func 함수를 호출하도록 해보면
부모 클래스의 func 함수를 호출하는 것을 알 수 있다.
원래 업케스팅을 한 이유는 부모 객체를 사용해서 자식 객체의 기능을 사용하는 기 위함인데 업케스팅을 했는데도 불구하고 부모 객체의 함수를 호출하는 것은 바라는가 아니다.
이런 상황에서 사용하는 것이 virtual 이라는 키워드이다.
자식 클래스에서 재정의한 부모 클래스의 함수에 virtual이라는 키워드를 붙여주면 업케스팅 시에 부모 클래스를 사용해서 호출하는 함수가 자식 클래스에서 재정의한 함수를 호출한것과 같은 기능을 갖게 된다
이게 virtual 함수이고 자식 클래스에서 재정의한 함수를 오버라이딩(overriding)했다고 한다.
그러면 이렇게 재정의한 자식 클래스를 상속하는 클래스의 경우는 어떨까?
보면 따로 virtual을 붙여주지 않았음에도 불구하고 virtual이 붙은 함수처럼 기능한다.
부모클래스를 상속한 자식 클래스의 경우는 오버라이딩 함수가 부모 클래스에서 virtual 함수로 변경되었을 때 상속 받은 자식클래스에서 따로 virtual이라는 키워드를 붙여주지 않더라도 virtual 함수로 변경된다.
그렇기에 자식 클래스가 다른 클래스를 상속 시킬때에도 업 케스팅 할때 자식의 자식 객체의 함수를 호출하게 되는 것이다.
보통 안붙여도 상관은 없지만 눈에 보일때 확실히 이 함수가 부모에서도 virtual이며 자식에서도 virtual이라고 알리기 위해 적어주는게 좋은 편이라고 한다.
그리고 사용자들이 이 함수들을 오버라이드 할때 휴먼 에러로 명칭을 틀린다거나 하는 경우가 많았기에
이걸 틀린 것을 찾는 것이 매우 힘들었을 것이다.
그래서 오버라이드 한 함수의 뒤에 override라고 붙여주면
이 함수가 오버라이드 했는데 부모의 가상함수를 오버라이드 하지 않을 경우 동일한 함수가 부모 객체에서는 생성되지 않았다고 컴파일러가 알리면서 해당 함수의 명칭이나 틀린점이 존재한다는 것을 인지 시킬 수 있다.
그렇기에 해당 함수가 부모에서 가상함수로 선언되지 않았다면 이 또한 에러를 발생시켜준다.
3. 가상 함수를 사용할 때 주의할 점
1) 시그니쳐가 다를 경우
가상 함수의 경우 시그니쳐가 다를 경우 오버라이딩 되지 않는다.
게다가 이렇게 같은 함수 명을 사용할 경우 부모 클래스에서 같은 이름에 호출하려는 함수가 존재함에도 불구하고 자식객체에서는 이 함수를 부를 수 없고 자신이 가진 함수만 부를 수 밖에 없게 된다.
이런 경우 함수가 가려졌다 라고 표현하는데 지금 위 상태가 부모에서는 func() 로 호출이 가능한 함수가 존재하는데도 불구하고 자식클래스에서는 func(int num)함수로 같은 명칭의 함수가 존재하기에 func()의 존재를 func(int num)으로 가려버린 상태로 호출할 수 없게 되는 것이다.
또한 함수를 const 함수로 변경했을 때에도 다른 함수로 취급을 한다
이는 this에 const가 붙으면서 매개변수의 타입이 그냥 this에서 const this로 변경되었기에 다른 함수로 인식하는 것이다.
만약 아래의 상태에서 부모의 get을 가리지 않고 자식 내부에서 사용하고 싶다면
이전에 using을 사용했던것과 같이 func만 가져와서 사용하면 사용이 가능해진다.
그리고 이 함수의 시그니쳐가 다른경우, 즉 함수의 형태가 다른 경우에는 오버라이드가 안된다고 했었는데 return 형이 부모-자식 관계에 있는 포인터나 레퍼런스인 경우에는 허용을 해준다.
이게 가능한 이유는 공변 반환 타입(Covariant Return Type)이기 때문이다.
오버라이딩할 때, 반환 타입이 부모 클래스의 반환 타입의 자식 타입이라면 허용하는 것으로 반환 타입이 부모 타입보다 더 구체적인 경우는 허용이 된다
가능한 조건은 함수가 virtual이거나 override해야 하고, 반환 타입이 포인터 or 참조여야 하고, 자식 클래스의 반환 타입이 부모 반환 타입을 상속한 타입이어야 한다.
2) 부모 클래스에 두가지 이상의 동일한 명칭의 함수가 존재할때( 부모에서 함수를 오버로딩 할때)
부모 클래스에서 이렇게 두개의 함수를 오버로딩 해서 생성했을때 자식 클래스에서 하나의 함수만 오버라이딩 한다면
부모 클래스의 두 함수를 모두 사용할 수 없고 오버라이딩 한 함수만 사용할 수 있게 된다.
이 또한 오버라이딩한 함수가 오버로딩한 함수를 가리게 되어서 발생하는 일이다.
이 부분도 주의해야 한다
물론 이때도 using을 사용해서 가져올 수 있다.
**가상함수와 별개로 상속에서 주의해야할 점
멤버 변수를 동일한 이름으로 선언하는 경우도 부모 클래스의 멤버 변수를 가린다.
부모 클래스에서 returnFunc라는 함수를 사용하면 멤버 변수 num을 반환하는 함수를 하나 선언하고
자식 클래스에서 동일한 명칭으로 num을 멤버 변수로 하나 선언하고 함수에서 해당 num을 20으로 변경 해준 뒤 부모의 returnFunc()를 호출해보면
부모의 num을 가리킨다면 10을 자식의 num을 가리킨다면 20을 출력할 것이다.
보면 자식 클래스의 num이 부모 클래스의 num을 가리고 출력 되는 것을 볼 수 있다.
'Programming Language > C++' 카테고리의 다른 글
Part2::Ch 03. 상속 - 03. 정적 결합, 동적 결합 (0) | 2025.05.04 |
---|---|
Part2::Ch 03. 상속 - 01. 상속의 기본 (0) | 2025.05.04 |
Part2::Ch 02. 연산자 오버로딩- 10. 사용자 정의 리터럴 (0) | 2025.05.03 |
Part2::Ch 02. 연산자 오버로딩- 09. 호출 연산자 오버로딩, 함수 객체 (0) | 2025.05.03 |
Part2::Ch 02. 연산자 오버로딩- 08. 변환 연산자 오버로딩, 변환 생성자, explicit (0) | 2025.05.03 |