한글 번역
Tip of the Week #126: make_unique
는 새로운 new
입니다
By James Dennett (jdennett@google.com),
Titus Winters (titus@google.com)의 메일링 리스트 게시글을 기반으로 작성
2016-12-12에 totw/126으로 최초 게시
코드베이스가 확장되면 모든 의존성의 세부 사항을 알기가 점점 어려워집니다. 심층적인 지식이 필요하도록 설계된 코드는 확장성이 떨어집니다. 대신 인터페이스와 계약을 통해 코드의 정확성을 보장해야 하며, 타입 시스템은 이러한 계약을 제공하는 데 큰 역할을 할 수 있습니다.
C++에서 동적 메모리 할당을 줄이기 위해 값 객체를 사용하는 것이 좋지만, 객체가 범위를 넘어 존재해야 하는 경우도 있습니다. 이때는 스마트 포인터(주로 std::unique_ptr
)를 사용해 동적 할당 객체의 소유권을 관리해야 합니다. 이는 소유권과 할당을 명확히 하며, 소유권 문제를 더 쉽게 확인할 수 있는 시각적 신호를 제공합니다. C++14 이후 표준과 일치하며 예외 안전성을 제공한다는 부가적인 장점도 있습니다.
왜 new
를 피해야 할까요?
스마트 포인터와 할당 함수가 new
나 raw 포인터보다 더 나은 이유는 다음과 같습니다:
-
타입 시스템을 통한 소유권 표현
타입 시스템을 통해 소유권을 표현하면, 코드 리뷰 시 누락된 할당 해제나 이중 삭제 문제를 쉽게 확인할 수 있습니다. -
의도 명확성
absl::make_unique()
는 의도를 명확히 표현하며, 할당과 소유권 전달 외에는 아무것도 하지 않습니다. 숨겨진 동작이나 타입 변환이 없습니다. -
타입 반복 제거
std::unique_ptr<T> my_t(new T(args));
처럼new
를 사용하면 타입 이름을 반복해야 하지만,absl::make_unique()
는 이를 제거합니다. -
코드 리뷰 간소화
absl::WrapUnique()
는 raw 포인터를std::unique_ptr
로 감싸는 데 사용되며, 리뷰 시 소유권이 이전되었는지 쉽게 확인할 수 있습니다. -
안전성 강화
std::unique_ptr<T> foo(Blah());
는 안전한지 여부가 함수Blah()
에 따라 다르지만,absl::make_unique()
를 사용하면 이러한 의문이 없습니다.
어떻게 선택할까요?
1. 기본적으로 absl::make_unique()
를 사용하세요.
std::make_shared()
는 소유권 공유가 필요한 경우에만 사용하세요. 예를 들어, 아래 코드는:
std::unique_ptr<T> bar(new T());
다음과 같이 작성하세요:
auto bar = absl::make_unique<T>();
2. 팩토리 함수에서는 absl::WrapUnique()
를 사용하세요.
비공개 생성자를 사용하는 팩토리 함수는 absl::WrapUnique(new T(...))
를 사용하여 std::unique_ptr<T>
를 반환하도록 작성합니다.
3. 중괄호 초기화가 필요한 경우 absl::WrapUnique()
를 사용하세요.
예를 들어 구조체나 배열, 컨테이너를 동적으로 할당할 때 사용합니다.
auto my_struct = absl::WrapUnique(new MyStruct{value1, value2});
4. 레거시 API와 상호작용 시
소유권을 T*
로 전달받거나 전달해야 하는 경우, absl::make_unique()
를 사용해 객체를 생성하고 release()
를 호출하거나 함수 인자에서 직접 new
를 사용할 수 있습니다.
5. 레거시 API에서 반환된 소유권을 처리할 때
레거시 API에서 소유권을 반환받으면 즉시 WrapUnique
를 사용해 스마트 포인터를 생성합니다.
요약
absl::make_unique()
를 기본적으로 사용하세요.- 필요할 때만
absl::WrapUnique()
를 사용하세요. new
는 가급적 사용하지 마세요.