본문 바로가기

C# 일기

9. 가상 함수와 오버라이드 (Virtual & Override)

8. 클래스 상속에서 배운대로 클래스를 상속시키면서 생기는 문제가 있다.

 

자식 클래스는 부모 클래스의 성질을 온전히 물려받게 되고, 거기서 확장되는 개념을 추가로 정의하게 되는데,

 

그렇다면 자식 클래스가 부모 클래스의 성질 중 하나를 상황에 맞게 변경 혹은 확장시킬 수는 없을까? 라는 문제이다.

 

예를들어, 아래와 같은 상속 클래스들이 있다고 가정해보자.

 

using System;

namespace Favor
{
   class Vehicle
   {
      public void Enter() { Console.WriteLine("탈것 안에 들어간다.");}
   }
   
   class Bike : Vehicle
   {
      public void BikeEnter() { Console.WriteLine("오토바이에 올라탄다."); }
   }
}

 

위와 같이 부모 클래스의 기능과 유사한 기능이지만, 상속받은 자식 클래스의 성질에 따라. 혹은, 상황에 따라 해당 내용을 일부 수정하거나, 혹은 확장해야할 경우가 생길 때가 있다.

 

그렇다고 위 처럼 새로운 함수를 또 하나 더 정의한다면, 앞으로 Vehicle 함수를 상속받는 자식 클래스가 Bike의 경우에 해당한다면, 모두 새로운 함수를 또 정의해야하여 코드의 재사용성이 매우 떨어지게 된다.

 

이를 해결하기 위해, 가상 함수라는 개념이 존재한다.

 

 

 

 

가상 함수란, 쉽게말해 부모 클래스에서 정의된 함수를 지정하여, 해당 함수를 자식 클래스에서 재정의할 수 있도록 만드는 것이다.

 

쉽게말해, 부모 클래스의 기능을 자식 클래스가 입맛에 맞게 바꿀 수 있다는 것이다.

 

사용 방법을 예로 들기 위해, 위의 Vehicle과 Bike의 경우에 맞게 가상 함수를 사용해보겠다.

 

using System;

namespace Favor
{
   class Vehicle
   {
      public virtual void Enter() { Console.WriteLine("차량에 탑승한다.");}
   }
   
   class Bike : Vehicle
   {
      public override void Enter() 
      {
         base.Enter();
         Console.WriteLine("안장에 올라탄다.");
      }
   }
   internal class Program
   {
      static void Main(string[] args)
      {
         Bike bike = new Bike();
         bike.Enter();
         
         // 출력값 : 차량에 탑승한다. 안장에 올라탄다.
      }
   }
}

 

가상함수를 선언하는 법은 크게 어렵지 않다.

 

기존에 함수를 선언하는 방식과 동일하지만, 자료형 앞에 virtual이라는 키워드를 붙여준다.

 

virtual은 영어로 가상이라는 의미로, 직관성이 뛰어나니 크게 헷갈리는 일이 없다.

 

이렇게 가상함수의 선언이 끝났으면, 해당 함수를 자식에서 재정의 하는 일만 남았다.

 

가상함수를 자식 클래스에서 재정의 하는 방법은, 동일한 함수 이름을 쓰되, 앞의 가상 함수 선언에서 virtual을 붙였던 것과 달리, override라는 키워드를 사용해 재정의 한다. 

 

위의 public virtual override Enter()처럼 말이다.

 

 

 

 

 

 

여기서 virtual과 override 말고 또 새롭게 등장한 base.라는 키워드가 등장했다.

 

base. 키워드는 자식 클래스가 상속받은 부모 클래스를 호출할 때 사용하는 키워드다.

 

따라서 base.Enter();를 호출했다면, 여기서는 Bike의 Enter()가 호출되는 것이 아닌, Vehicle의 Enter()가 호출된다.

 

위처럼 함수를 불러오는 것 외에도, 다른 부모의 기능이나 요소들에 접근하는 것이 가능한데, 앞서 접근제한자를 통해 배웠다싶이 private 멤버에는 접근할 수 없고, 자식에게 허용된 public이나 protected 멤버에만 접근이 가능하다.

 

이러한 특성은 코드의 재사용성을 높여주기 때문에 굉장히 유용하다.

 

 

 

물론 가상 함수라고 해서 반드시 자식 클래스에서 재정의하여 사용해야하는 것은 아니다.

 

상황에 따라 재정의가 가능하다는 것일 뿐, 해당 객체가 굳이 부모 클래스의 기능을 고치지 않고 그대로 사용해도 된다면 재정의 하지않고 그대로 사용할 수 있다.

 

 

 

 

 

부모에게 늘 받기만 하던 자식 클래스가 이제는 독립적으로 부모가 알려준 것을 상황에 따라 다르게 적용시켜도 보고,

 

때로는 자신만의 기능으로 다시 만들기도 할 정도로 대견한 아이로 자랐다.

 

다음에는 자식 클래스가 더욱 독립적으로 자라게 되는 추상 클래스에 대해 알아보도록 하겠다.

'C# 일기' 카테고리의 다른 글

11. 인터페이스 (Interface)  (2) 2024.03.06
10. 추상 클래스(Abstract Class)  (2) 2024.03.06
8. 클래스 상속  (0) 2024.03.05
7. 프로퍼티(Property)  (0) 2024.03.05
6. 정적 멤버 (static)  (0) 2024.03.05