아래는 “이번 주의 팁 #234: 값, 포인터, 참조로 전달하기”에 대한 한글 번역입니다.
제목: “이번 주의 팁 #234: 값, 포인터, 참조로 전달하기”
원문 게시일: 2024년 8월 29일
업데이트: 2024년 9월 30일
작성자: Steve Wang
빠른 링크: abseil.io/tips/234
개요
C++는 함수의 입력 인자를 전달하는 다양한 방식을 제공합니다:
- 값으로 전달(pass by value): 호출된 함수가 인자의 복사본을 받습니다.
- 참조로 전달(pass by reference): 호출된 함수가 호출자의 객체를 참조합니다.
- 포인터로 전달(pass by pointer): 호출된 함수가 객체의 주소를 통해 접근합니다.
이 팁에서는 입력 전용 함수 인자를 전달하는 방법과 각각의 장단점을 살펴보며, 적절한 사용 지침을 제공합니다.
1. 값으로 전달
값으로 전달하면 함수는 인자의 복사본을 받으며, 호출자의 원본 객체에는 영향을 주지 않습니다. 그러나 함수 내에서 복사본의 메서드를 호출하면 객체의 내부 상태를 변경할 수 있습니다.
void AddOneToValue(int x) {
++x;
}
int x = 5;
AddOneToValue(x);
// x는 여전히 5입니다.
값으로 전달은 다음과 같은 상황에서 유용합니다:
- 작고 간단한 타입: 복사 비용이 낮은 경우.
- 독점 소유권 필요: 함수가 복사본의 소유권을 가져야 하는 경우.
값으로 전달은 스택에 저장된 값(레지스터를 사용하는 경우 포함)에 대해 더 효율적일 수 있습니다. 예를 들어, 정수와 같은 작은 타입은 레지스터를 통해 전달됩니다.
2. 참조로 전달
참조로 전달하면 함수가 호출자의 객체를 참조하므로, 함수 내부에서 객체를 수정할 수 있습니다. const
키워드를 사용해 객체를 읽기 전용으로 보호할 수도 있습니다.
void AddOneToReference(int& x) {
++x;
}
int x = 5;
AddOneToReference(x);
// x는 6이 됩니다.
참조로 전달은 다음과 같은 상황에서 유용합니다:
- 객체를 복사하는 비용이 큰 경우.
- 호출자와 동일한 객체를 참조해야 하는 경우.
그러나 참조로 전달은 별칭(aliasing) 문제를 일으킬 수 있습니다. 함수 호출 중 객체 상태가 외부에서 변경될 수 있기 때문입니다.
3. 포인터로 전달
포인터로 전달은 참조로 전달과 유사하지만, null 포인터를 사용해 “값이 없음” 상태를 나타낼 수 있다는 점이 다릅니다.
void AddOneToPointee(int* x) {
++(*x);
}
int x = 5;
AddOneToPointee(&x);
// x는 6이 됩니다.
포인터 전달은 선택적 인자(optional parameter)에 적합하며, 값이 없음을 명시적으로 나타내야 할 때 유용합니다.
사용 지침
값으로 전달
값으로 전달은 다음과 같은 경우 권장됩니다:
- 작은 타입 (예: 숫자, 열거형).
- 소유권 이동이 필요한 경우 (예: 스마트 포인터).
특히 std::vector
, std::string
과 같은 타입은 값으로 전달하고, 필요 시 호출부에서 std::move
를 사용하세요:
class Foo {
public:
Foo(std::vector<int> bar) : bar_(std::move(bar)) {}
private:
std::vector<int> bar_;
};
참조 또는 포인터로 전달
참조 또는 포인터로 전달은 다음과 같은 경우 적합합니다:
- 큰 객체: 복사 비용이 높은 경우.
- 프로토콜 버퍼: 프로토콜 버퍼 객체는 비싼 복사가 발생하므로 항상 참조로 전달합니다.
선택적 인자는 포인터를 사용해 null 상태를 명시적으로 처리하는 것이 좋습니다.
뷰(View) 타입으로 전달
뷰 타입(예: absl::string_view
, absl::Span<const T>
)은 읽기 전용 데이터에 적합합니다:
- 문자열:
absl::string_view
를 사용해 다양한 입력 타입을 처리할 수 있습니다. - 컨테이너:
absl::Span<const T>
를 사용해 벡터와 유사한 데이터 구조를 처리할 수 있습니다.
요약
- 값으로 전달: 작은 타입 또는 소유권 이동이 필요한 경우.
- 참조로 전달: 큰 객체 또는 복사 비용이 높은 경우.
- 포인터로 전달: 선택적 인자에 적합.
- 뷰 타입: 읽기 전용 데이터 처리에 효율적.
기본적으로 const&
를 사용하되, 성능 프로파일링을 통해 필요한 경우 지침에서 벗어나는 것도 가능합니다.