[C++/UE5] 가상 함수부터 인터페이스까지: 객체지향의 핵심 정리

C++ 개발자라면 반드시 마스터해야 할 순수 가상 함수, 추상 클래스, 인터페이스, 그리고 가상 소멸자의 개념을 실무적 관점에서 정리합니다.

1. 순수 가상 함수(Pure Virtual Function)와 추상 클래스

● 교과서적 정의

  • 순수 가상 함수: 함수의 선언 뒤에 = 0;을 붙여 구현부를 비워둔 함수입니다. "이 기능은 자식 클래스에서 반드시 구현하라"는 강제성을 부여합니다.
  • 추상 클래스: 순수 가상 함수가 하나라도 포함된 클래스입니다. 설계도 역할을 하며, new Base()와 같이 객체를 직접 생성(인스턴스화)할 수 없습니다.
[심화] 순수 가상 함수도 구현부(Body)를 가질 수 있을까?
네, 가능합니다. virtual void Func() = 0;으로 선언하더라도 .cpp 파일에서 본체를 정의할 수 있습니다. 이는 자식 클래스들이 오버라이딩을 하되, 부모의 공통 로직을 Base::Func() 형태로 호출하여 재사용하고자 할 때 사용됩니다.
💡 면접 포인트: "순수 가상 함수를 결정짓는 기준은 구현부의 유무가 아니라 선언부의 = 0 키워드입니다. 자식이 이를 구현하지 않으면 자식 또한 추상 클래스가 되어 인스턴스화가 불가능해집니다."

2. 다중 상속과 인터페이스(Interface)

● 다중 상속의 위험성: 죽음의 다이아몬드(Deadly Diamond)

C++은 여러 클래스를 동시에 상속받는 다중 상속을 지원하지만, 실무에서는 다음과 같은 이유로 지양됩니다.

  1. 모호성(Ambiguity): 여러 부모가 동일한 이름의 함수를 가질 경우 어떤 것을 호출할지 불분명해집니다.
  2. 메모리 중복: 공통 조상 클래스의 멤버가 중복 생성되어 메모리가 낭비됩니다.

● 언리얼 엔진의 솔루션: UInterface

언리얼은 안전한 다중 상속 효과를 위해 UInterface 시스템을 사용합니다.

  • IClassName: 실제 기능을 정의하고 오버라이딩하는 순수 C++ 인터페이스 부분입니다.
  • UClassName: 엔진 리플렉션과 블루프린트 호환성을 위한 관리용 클래스입니다.
💡 면접 포인트: "상속은 Is-A(정체성) 관계를, 인터페이스는 Can-Do(기능) 관계를 정의합니다. 수직적인 상속 계층이 깊어지는 것을 방지하고, 수평적으로 기능을 확장할 때 인터페이스가 강력한 대안이 됩니다."

3. 가상 소멸자(Virtual Destructor)

● 왜 필수인가?

부모 클래스 포인터로 자식 객체를 가리키는 다형성을 사용할 때, 부모 소멸자에 virtual이 없으면 delete자식의 소멸자가 호출되지 않습니다. 이는 자식 클래스에서 할당한 메모리나 자원(TArray, 포인터 등)이 해제되지 않는 치명적인 메모리 누수를 야기합니다.

● 모바일 환경에서의 중요성

Android/iOS 등 리소스가 제한적인 모바일 환경에서는 작은 메모리 누수가 앱 전체의 크래시(OOM)로 이어질 수 있습니다. 특히 VTable을 통한 가상 함수 호출 비용보다, 올바른 해제를 통한 메모리 안전성 확보가 훨씬 중요합니다.

💡 면접 포인트: "언리얼의 UObject 계열은 이미 최상위 부모에 가상 소멸자가 있어 안전하지만, 순수 C++ 클래스나 외부 라이브러리 연동 시에는 반드시 가상 소멸자를 명시하여 다형성 파괴 상황에 대비해야 합니다."

포스팅이 도움이 되셨다면 공감과 댓글 부탁드립니다! :)