C++은 private virtual이 가능합니다

JellyPower·2023년 4월 15일
0

나만 몰랐던 C++

목록 보기
2/11
post-thumbnail

private, protected

  • 우리가 어떤 언어를 배우든, private은 해당 클래스 내에서만 접근 가능하고, protected는 상속관계일 때 접근이 가능하다고 배운다.
  • 그리고 이것은 대부분의 객체지향에서 단순 접근이 아니라 정의에서도 통용되는 말이다. 생각해보면 당연하다. 내가 호출할 수 없는 함수를 내가 정의한다는것이 가당키나 한가?
  • 그래서 대부분의 객체지향 언어에서는 private virtual 자체를 막아놓는다.

실제로 C#에선 위처럼 컴파일 에러를 던져준다.

C++은 합니다!

  • 그런데 우리의 C++은 private virtual이 가능하다!
  • 이걸 가능하게 해봤자 의미가 있나? 싶을수 있는데 곰곰히 생각해보면 그렇게 의미없는 놈은 아니다.
  • 내가 위에서 “private은 해당 클래스 내에서만 접근 가능하고, protected는 상속관계일 때 접근이 가능하다. 그리고 이것은 대부분의 객체지향 언어에서 단순 접근이 아니라 정의에서도 통용되는 말이다”라고 말했던게 기억이 나는가?
  • 실제로 접근지정자의 개념에서 접근정의를 분리한다면 private virtual은 충분히 가능하다. 어디서 접근할지는 Base클래스가 정해도 해당 함수가 무엇을 할지에 대한 정의는 자식 클래스에게 맡기는 관점으로 가는 것이다.

실전 예제

#include<cstdio>

//================== Base ==================
class Base {
private:
	virtual int retInt() {
		return 1;
	}

public:
	void printInt() {
		printf("%d\n", retInt());
	}
};

//================== Derived ==================
class Derived : public Base {
private:
	virtual int retInt() override {
		return 2;
	}
};

//================== main ==================
int main() {

	Base b;
	Derived d;

	b.printInt(); // 1출력
	d.printInt(); // 2출력

	return 0;
}

비가상 함수 인터페이스(NVI)

  • 위의 예제처럼 public 비가상 멤버함수를 통해 private 가상함수를 간접적으로 호출하게 하는 방식을 비가상 함수 인터페이스(NVI) 관용구라고 부르고 이는 템플릿 메서드(템플릿 클래스에서 템플릿 아님)라는 고전적인 디자인 패턴을 C++식으로 구현한 것이다.
  • 그리고 템플릿 메서드에 대한 대표적인 예시로 언리얼의 Tick()함수와 유니티의 Update()함수를 들 수 있다.
    • 해당 함수들은 게임엔진이 매 프레임마다 한 번 씩 호출하도록 정의된 함수들이다.

    • 게임엔진은 프레임마다 유저 모르게 게임 내의 하나의 오브젝트들에 대해 엄청나게 많은 처리를 한다. 그런데 유저는 Tick(), Update()함수의 앞뒤에 무슨 일을 해야하는지에 대해 몰라도 해당 함수들만 오버라이딩 하면 원하는 게임 로직을 만들 수 있다.

      사실 유니티의 Update() 함수는 private멤버를 public멤버가 호출하는 형식이 아니라 리플렉션을 활용하긴 하지만… 어쨋든 내가 정의한 함수 이외의 일들은 엔진이 알아서 처리해준다는 관점에서 템플릿 메서드 패턴과 유사하다 볼 수 있다.

추가적인 이야기

  • 사실NVI관용구는 엄격하게 private일 필요는 없다. 그러니 다른 언어들이 private virtual을 막아놓는 것도 합리적인 언어설계라고 생각할 수 있다.

레퍼런스

Private virtual method in C++

Virtuality

profile
게임엔진코드싸개(진)

0개의 댓글