클래스의 Virtual 메서드

클래스의 Virtual 메서드 (Virtual methods)

클래스는 객체처럼 virtual 메서드를 가져요. 다만 둘 사이에는 차이가 있어요. 객체의 경우 자손 객체에서 같은 메서드를 virtual 키워드로 재선언하면 오버라이드하기에 충분해요. 클래스는 상황이 달라요. virtual 메서드는 override 키워드로 오버라이드해야 해요. 그렇게 하지 않으면 새 virtual 메서드 묶음이 시작되면서 이전 것을 숨겨버려요. Virtual을 사용했다면 Inherited 키워드가 상속된 메서드로 점프하지 않아요.

출처: Virtual methods

본문

다음 코드는 잘못됐어요.

Type
  ObjParent = Class
    Procedure MyProc; virtual;
  end;
  ObjChild  = Class(ObjPArent)
    Procedure MyProc; virtual;
  end;

컴파일러는 경고를 만들어요.

Warning: An inherited method is hidden by OBJCHILD.MYPROC

컴파일러는 컴파일하지만, Inherited를 쓰면 이상한 효과가 생길 수 있어요.

올바른 선언은 이렇습니다.

Type
  ObjParent = Class
    Procedure MyProc; virtual;
  end;
  ObjChild  = Class(ObjPArent)
    Procedure MyProc; override;
  end;

이것은 경고나 에러 없이 컴파일되고 실행돼요.

virtual 메서드를 정말로 같은 이름의 다른 메서드로 교체해야 한다면 reintroduce 키워드를 쓸 수 있어요.

Type
  ObjParent = Class
    Procedure MyProc; virtual;
  end;
  ObjChild  = Class(ObjPArent)
    Procedure MyProc; reintroduce;
  end;

이 새 메서드는 더 이상 virtual이 아니에요.

이게 가능하려면 컴파일러는 클래스 타입마다 virtual 메서드 테이블, 즉 VMT(Virtual Method Table)을 유지해요. 이건 단순히 각 virtual 메서드를 가리키는 포인터들의 테이블이에요. 각 virtual 메서드는 이 테이블에서 고정된 위치(인덱스)를 가져요. 컴파일러는 이 테이블로 런타임에 실제로 사용할 메서드를 조회해요. 자손 객체가 메서드를 오버라이드하면 부모 메서드 항목이 VMT에서 덮어써져요. VMT에 대한 자세한 내용은 프로그래머 가이드에 있어요.

Remark: "virtual" 키워드는 "dynamic" 키워드로 대체할 수 있어요. dynamic 메서드는 virtual 메서드와 똑같이 동작해요. Delphi와 달리 FPC에서는 dynamic 메서드의 구현이 virtual 메서드의 구현과 동일해요.

더 알아보기

클래스 virtual 메서드는 override로 오버라이드해야 해요. virtual을 그대로 쓰면 새 메서드 묶음이 시작되며 이전 메서드를 숨기는 게 핵심이에요.