C++에는 포인터가 존재한다.
이 포인터 변수를 통해 다른 변수(lvalue)가 가진 값(rvalue)을 참조할 수 있다.
그리고 이 포인터 변수의 앞에 * 연산자를 놓으면 포인터가 참조하는 변수에 역참조할 수 있다.
이 시간에는 연산자 *(Asterisk, 별표)의 사용법을 알아본다.
코드는 언리얼 엔진에서 사용하는 C++ 클래스이다.
1. 포인터 변수의 역참조 연산자
// Mover.cpp
void UMover::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
AActor* Owner = GetOwner();
// ...
}
// ActorComponent.h
FORCEINLINE_DEBUGGABLE AActor* UActorComponent::GetOwner() const
{
#if WITH_EDITOR
// During undo/redo the cached owner is unreliable so just used GetTypedOuter
if (bCanUseCachedOwner)
{
checkSlow(OwnerPrivate == GetActorOwnerNoninline()); // verify cached value is correct
return OwnerPrivate;
}
else
{
return GetActorOwnerNoninline();
}
#else
checkSlow(OwnerPrivate == GetActorOwnerNoninline()); // verify cached value is correct
return OwnerPrivate;
#endif
}
AActor 타입 변수 Owner을 포인터 변수로 선언했다. GetOwner() 이라는 함수는 *AActor 타입으로 반환한다.
여기서 연산자 * 는 Owner 변수가 참조하는 값을 역참조하는 용도로 사용된다.
*Owner 의 값은 곧 GetOwner()가 반환한 포인터가 가리키는 값이다.
2. 연산자 오버로딩
// Mover.cpp
void UMover::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
AActor* Owner = GetOwner();
FString Name = Owner->GetActorNameOrLabel();
UE_LOG(LogTemp, Display, TEXT("%s"), *Name);
// ...
}
// UnrealString.h
/**
* Get pointer to the string
*
* @Return Pointer to Array of TCHAR if Num, otherwise the empty string
*/
UE_NODISCARD FORCEINLINE const TCHAR* operator*() const UE_LIFETIMEBOUND
{
return Data.Num() ? Data.GetData() : TEXT("");
}
대부분의 프로그래밍 언어가 지원하듯 C++에서 연산자 오버로딩을 할 수 있다.
FString Name은 그 자체로 string 타입이 아니다. 내부에 있는 Data가 존재하며 이 Data가 흔히 생각하는 string 타입으로 문자열로써 출력될 수 있다.
그리고 이 Data에 접근할 수 있는게 위의 operator* 함수에서 재정의되어있다.
*Name을 통해 Name의 Data에 접근하여 문자열로 출력할 수 있는 것이다.
연산자 * 의 오버로딩은 std::string::iterator에서도 볼 수 있다.
string의 iterator와 관련한 클래스는
String_view_iterator
_String_const_iterator
_String_iterator
세 가지를 찾아볼 수 있으며 xstring 헤더파일에 정의되어 있다.
그 중 std::string::iterator의 실질적 타입인 _String_iterator의 부모 클래스 _String_const_iterator를 살펴보자.
(_String_iterator 클래스에서는 const_cast로 부모의 오버로딩 함수를 오버라이딩해서 사용하고 있다. 실제 함수의 원문은 부모 클래스를 살펴보아야 한다.))
_String_const_iterator가 가지고 있는 연산자 * 의 오버로딩 함수를 보면
// xstring
// _String_const_iterator class
_NODISCARD _CONSTEXPR20 reference operator*() const noexcept {
#if _ITERATOR_DEBUG_LEVEL >= 1
_STL_VERIFY(_Ptr, "cannot dereference value-initialized string iterator");
const auto _Mycont = static_cast<const _Mystr*>(this->_Getcont());
_STL_VERIFY(_Mycont, "cannot dereference string iterator because the iterator was"
" invalidated (e.g. reallocation occurred, or the string was destroyed)");
const auto _Contptr = _Mycont->_Myptr();
const auto _Rawptr = _Unfancy(_Ptr);
_STL_VERIFY(_Contptr <= _Rawptr && _Rawptr < _Contptr + _Mycont->_Mysize,
"cannot dereference string iterator because it is out of range (e.g. an end iterator)");
#endif // _ITERATOR_DEBUG_LEVEL >= 1
_Analysis_assume_(_Ptr);
return *_Ptr;
}
이런식으로 실제 string 값에 속하는 *_Myptr을 반환하는 것을 볼 수 있다.
std::string::iterator it 로 it 변수를 선언하면
*it 는 포인터의 역참조가 아닌 연산자 오버로딩 개념이 되는 것이다.
'Programming Languages > C++ Study' 카테고리의 다른 글
C++ MultiThread) std::atomic과 SpinLock (4) | 2024.03.06 |
---|---|
C++) 범위 기반 for문(Range based for loop)과 사용 조건 (0) | 2024.01.26 |
C++) RTTI와 Dynamic Cast (1) | 2024.01.25 |
C++) _CrtIsValidHeapPointer(block) (0) | 2024.01.22 |