안선생의 개발 블로그

언리얼 포인터 및 참조 본문

언리얼

언리얼 포인터 및 참조

안선생 2024. 1. 1. 22:35

언리얼은 C++기반으로 만들어진 상용엔진이다.

 

C++을 공부할 때 함수 매개변수에 객체를 전달할라 할 때 참조를 이용해야 절약이 된다는 소리를 많이 들었습니다.

하지만 언리얼은 객체를 전달 할 때 포인터를 사용하여 전달합니다. 

그래서 한번 알아봤습니다.

 

먼저 데이터를 함수에 전달하는 방법에는 3가지가 있습니다. 값 기준(기호 없음), 참조 기준(& 기호 포함) 및 포인터 기준(* 기호 포함)이 있습니다. 

 

따라서 이러한 함수 변수 유형을 사용하는 이유를 이해하려면 먼저 함수가 실제로 작동하는 방식을 이해해야 합니다. 함수를 호출하면 프로그램은 8바이트(함수 주소)를 스택에 배치합니다. 이는 프로그램 코드에서 현재 위치를 추적합니다. 함수에 매개변수가 없으면 해당 8바이트만 스택에 푸시되고 프로그램이 완료되면 해제됩니다.

 

하지만 함수에 매개변수가 있다면 매개변수도 스택에 푸시됩니다. 일반 int나 float은 4바이트를 활용할것입니다. 별차이 없을진 몰라도 객체가 매개변수가 들어온다면 말이 달라집니다. 이 상황에서 얼마나 많은 데이터가 스택에 배치되는지는 해당 객체의 크기에 따라 결정됩니다. 매개변수가 많다면 전부다 복사하겠죠? 그럼 그순간에 낭비가 될것입니다.

또 문제점은 이동되지도 않고 참조되지도 않습니다. 중복으로 가득 차 있습니다.  

 

그러면 사용할 수 있는거는 포인터와 참조입니다.

 

참조(&) 또는 포인터(*)로 객체를 전달하는 경우 스택에 8바이트를 배치합니다. 기본 개체의 크기에 관계없이. 왜? 실제로 전체 개체를 스택에 넣는 것이 아니라 단순히 해당 개체에 주소를 제공하는 것입니다. 이렇게 하면 아무리 큰 객체여도 8바이트마깨 안 쓰겠죠 이렇게 하면 더 큰 물체를 지나갈 때 많은 공간이 절약됩니다! 

 

음 하지만 FVector같은 8바이트보다 낮은 구조체가 있습니다. 이럴때는 포인터를 쓰면 값이 더 비싸죠?

그렇다면 첫번째 방식인 복사로 하면 5바이트정도로 복사할 수 있는데 이것 마져도 아낄 수 있습니다 . 

이럴 때 참조를 쓰면 복사비용이 없어집니다. 이 개체를 복사하지 말고 대신 직접 전달된 이 개체를 사용하세요!"라고 말하는 것입니다. 이것은 C++의 매우 유용한 기능이며 객체를 함수에 전달하는 것과 같은 간단한 작업을 수행할 때 많은 시간과 메모리를 절약할 수 있습니다. 모든 함수 매개변수를 const TArray<T>&문자열 로 변환하면 const FString&제가 작업한 대규모 게임에서 성능이 25% 향상되었습니다.

 

 

그런데 여기서 문제점은 참조로 전달할 시 호출자의 해당 데이터 버전을 수정할 수 있다는 것입니다. 참조는 그 데이터의 별명이라고 생각하시면 됩니다.

모든 변경 사항이 호출에에 영향을 미칩니다. 이는 참조로 전달하는 데 정말 유용한 속성이지만 그런 일이 발생하는 것을 매우 원하지 않는 경우가 있습니다. By Value를 전달하는 것은 엄청나게 비용이 많이 들 수 있으므로 이를 방지하기 위해 수행할 수 있는 작업은 const매개변수에 수정자를 추가하는 것입니다. 그러면 안전하게 사용할 수 있습니다.

 

그럼 왜 객체를 넘길 때 포인터를 사용할가요?

저는 이게 참 궁금했는데 이유는 간단했습니다. 포인터는 Null체크를 할 수 있고, 참조는 Null체크를 할 수 없습니다!

이거는 참 중요한대요

포인터가 여전히 유효한 데이터를 가리키는지 여부를 언제든지 알 수 없기 때문에 이렇게 해야 합니다.

포인터가 유효하지 않은 경우 검사를 사용하면 의도적으로 게임이 중단됩니다. 코드의 특정 지점에서 포인터가 유효하지 않은 것이 절대적으로 중요하다고 생각하는 경우 이 작업을 수행할 수 있습니다.

함수에서 복귀하는 경우 종료할 때 화면이나 로그 메시지를 인쇄하지 않으면 감지할 수 없는 "자동" 실패 사례가 될 수 있습니다.

check 메소드를 사용하면 무슨 일이 일어났는지 알 수 있습니다. 즉, 절대적으로 유효해야 하는 포인터가 유효하지 않다는 것입니다.

 

포인터 (*):

  • null일 수 있습니다.
  • 메모리 주소를 가지고 있습니다.
  • 이전에 가리킨 개체가 아닌 다른 개체를 가리키도록 변경할 수 있습니다.
  • 참조를 가리킬 수는 없습니다.

참조 (&):

  • 포인터나 메모리 자체의 개체를 참조할 수 있습니다.
  • 주소가 없으므로 포인터를 할당할 수 없습니다.
  • 일단 선언하면 다른 것을 참조하도록 변경할 수 없습니다.

 

포인터를 사용하는 이유는 무엇입니까?

  • 포인터는 또한 해당 데이터의 복사본을 만들지 않고도 엄청난 양의 데이터에 액세스할 수 있는 방법을 제공합니다.

 

 

 

참조로 데이터 전달

&의 또 다른 용도는 참조를 통해 함수에 데이터를 전달하는 것입니다.

이는 함수 컨텍스트에 복사하고 싶지 않은 매우 큰 양의 데이터에 특히 중요합니다!

 

int32 AMyClass::GetArraySize(TArray&amp; MyHugeBinaryArray) const 
{ 
	return MyHugeBinaryArray.Num(); //you use . instead of-> because passing by Reference
}

&를 사용하지 않으면 그것이 얼마나 큰지 알아보기 위해 거대한 배열 전체를 복사하게 됩니다!

 

 

 

 

'언리얼' 카테고리의 다른 글

[UE5] 언리얼 커스텀 로그 만들기  (1) 2024.11.14
[UE5] 모듈과 플러그인  (1) 2024.11.12
[UE5] 애니메이션 리타겟팅  (0) 2023.12.22
[UE5] 언리얼 Mixamo 루트모션 적용  (1) 2023.12.21
[UE5] 리플렉션  (0) 2023.12.14