CPP 포인터/레퍼런스 사용 이유
포인터 레퍼런스 사용처
포인터와 레퍼런스를 언제 써야 할까?
포인터와 레퍼런스 사용처
개인적으로는 다음 기준으로 구분하면 이해하기 쉽다.
“값이 없을 수 있으면 포인터, 반드시 존재해야 하면 레퍼런스”
레퍼런스는 생성되는 순간 반드시 유효한 객체를 참조해야 하며, nullptr 상태가 될 수 없다.
반면 포인터는 객체를 가리키지 않는 상태를 표현할 수 있다.
예를 들어 다음과 같은 경우에는 포인터가 적합하다.
- 객체가 아직 생성되지 않은 경우
- 객체 생성에 실패할 수 있는 경우
- 라이브러리 함수 호출 결과가 없을 수 있는 경우
- 특정 상황에서 참조 대상이 존재하지 않을 수 있는 경우
1
AActor* TargetActor = nullptr;
이처럼 포인터는 nullptr을 통해 “대상이 없다”는 상태를 표현할 수 있다.
그렇다면 “모든 것을 포인터로 사용하면 되는 것 아닌가?”라는 생각이 들 수 있다.
물론 가능은 하지만, 레퍼런스를 사용하는 이유도 분명히 존재한다.
1. 반드시 존재하는 값이라는 의도를 표현
1
void MoveCharacter(ACharacter& Character);
함수를 사용하는 입장에서는 Character가 절대로 nullptr일 수 없다는 사실을 바로 알 수 있다.
반면 포인터는 항상 유효성 검사를 고려해야 한다.
1
void MoveCharacter(ACharacter* Character);
1
2
3
4
if (Character)
{
Character->Move();
}
즉, 레퍼런스는 “이 값은 반드시 존재한다”는 의미를 코드 자체로 표현할 수 있다.
2. 사용 문법이 더 자연스러움
포인터는 멤버 접근 시 -> 연산자를 사용한다.
1
Character->Move();
레퍼런스는 일반 객체와 동일하게 사용할 수 있다.
1
Character.Move();
덕분에 코드가 조금 더 직관적으로 보인다.
3. 불필요한 null 체크
포인터를 사용하면 항상 nullptr 가능성을 고려해야 한다.
반면 레퍼런스는 유효한 객체가 전달된다는 전제하에 사용되므로, 매번 null 체크를 작성할 필요가 없다.
이는 코드의 가독성을 높이고 실수를 줄이는 데 도움이 된다.
4. 메모리 관리와의 관계
레퍼런스를 사용한다고 해서 메모리가 자동으로 해제되는 것은 아니다.
1
2
AActor Actor;
AActor& Ref = Actor;
레퍼런스는 단순히 기존 객체의 별명(Alias)일 뿐이다.
객체의 생성과 소멸을 관리하지 않으며, 메모리 해제 여부와도 직접적인 관련이 없다.
따라서 “레퍼런스를 사용하면 메모리 누수가 발생하지 않는다”는 표현은 정확하지 않다.
메모리 누수는 객체의 소유권(Ownership)을 어떻게 관리하느냐에 따라 결정된다.
정리
레퍼런스를 사용하는 경우
- 반드시 값이 존재해야 함
- 함수가 유효한 객체를 요구함
- null 상태를 표현할 필요가 없음
- 코드의 의도를 명확하게 표현하고 싶음
포인터를 사용하는 경우
- 값이 없을 수 있음
- 선택적(Optional)인 객체임
- null 상태를 표현해야 함
- 객체 생성 여부가 런타임에 결정됨
한 줄로 정리하면,
“없을 수 있으면 포인터, 반드시 있어야 하면 레퍼런스”
라고 생각하면 대부분의 상황에서 적절한 선택을 할 수 있다.