std:: unique_ptr
|
헤더 파일에 정의됨
<memory>
|
||
|
template
<
class
T,
|
(1) | (C++11부터) |
|
template
<
class
T,
|
(2) | (C++11부터) |
std::unique_ptr
는 다른 객체를 포인터를 통해 소유하고 관리하는 스마트 포인터이며,
unique_ptr
이 스코프를 벗어날 때 해당 객체를 자동으로 처리합니다.
객체는 다음 중 하나가 발생할 때 연관된 삭제자를 사용하여 폐기됩니다:
객체는 사용자가 제공한 삭제자를 사용하여
get_deleter
(
)
(
ptr
)
를 호출하여 처리됩니다. 기본 삭제자(
std::default_delete
)는
delete
연산자를 사용하며, 이는 객체를 파괴하고 메모리를 해제합니다.
A
unique_ptr
는 객체를 소유하지 않을 수도 있으며, 이 경우
empty
로 설명됩니다.
unique_ptr
에는 두 가지 버전이 있습니다:
- 단일 객체를 관리합니다 (예: new 로 할당된 객체).
- 객체의 동적 할당 배열을 관리합니다 (예: new [ ] 로 할당된 배열).
이 클래스는 MoveConstructible 과 MoveAssignable 요구사항을 만족하지만, CopyConstructible 과 CopyAssignable 요구사항은 만족하지 않습니다.
만약
T*
가 유효한 타입이 아닌 경우(예:
T
가 참조 타입인 경우),
std
::
unique_ptr
<
T, Deleter
>
의 정의를 인스턴스화하는 프로그램은 형식에 맞지 않습니다.
| 타입 요구사항 | ||
-
Deleter
는
FunctionObject
이거나
FunctionObject
에 대한 좌측값 참조이거나 함수에 대한 좌측값 참조여야 하며,
unique_ptr
<
T, Deleter
>
::
pointer
타입의 인수로 호출 가능해야 합니다.
|
목차 |
참고 사항
오직 비-상수
unique_ptr
만이 관리 객체의 소유권을 다른
unique_ptr
로 이전할 수 있습니다. 객체의 수명이
const
std
::
unique_ptr
에 의해 관리되는 경우, 해당 객체의 수명은 포인터가 생성된 범위로 제한됩니다.
unique_ptr
는 일반적으로 다음과 같은 객체의 수명을 관리하는 데 사용됩니다:
- 동적 수명을 가진 객체를 처리하는 클래스와 함수에 예외 안전성을 제공하며, 정상 종료 시와 예외를 통한 종료 시 모두 삭제를 보장합니다.
- 동적 수명을 가진 유일하게 소유된 객체의 소유권을 함수로 전달하기.
- 동적 수명을 가진 고유 소유 객체를 함수로부터 획득하기.
- 이동 인식 컨테이너(예: 다형적 동작이 필요한 경우 동적으로 할당된 객체에 대한 포인터를 보유하는 std::vector 와 같은 컨테이너)에서 요소 타입으로 사용됩니다.
unique_ptr
는
불완전한 타입
T
에 대해 생성될 수 있으며, 이는
pImpl 관용구
에서 핸들로 사용하기 용이하게 합니다. 기본 삭제자를 사용하는 경우,
T
는 삭제자가 호출되는 코드 지점에서 완전한 타입이어야 합니다. 이는
unique_ptr
의 소멸자, 이동 할당 연산자, 그리고
reset
멤버 함수에서 발생합니다. (대조적으로,
std::shared_ptr
는 불완전한 타입에 대한 원시 포인터로부터 생성될 수 없지만,
T
가 불완전한 상태에서 소멸될 수 있습니다).
T
가 클래스 템플릿 특수화인 경우,
unique_ptr
를 피연산자로 사용하는 것(예:
!
p
)은
ADL
로 인해
T
의 매개변수가 완전한 타입이어야 함을 유의하십시오.
만약
T
가 어떤 기본 클래스
B
의
파생 클래스
라면,
unique_ptr
<
T
>
는
unique_ptr
<
B
>
로
암시적으로 변환 가능
합니다. 결과로 생성된
unique_ptr
<
B
>
의 기본 삭제자는
B
에 대한
operator delete
를 사용하므로,
B
의 소멸자가
가상
이 아닌 경우
정의되지 않은 동작
을 초래합니다.
std::shared_ptr
은 다르게 동작한다는 점에 유의하세요:
std::
shared_ptr
<
B
>
는
T
타입에 대한
operator delete
를 사용하며,
B
의 소멸자가
가상
이 아니더라도 소유된 객체가 올바르게 삭제됩니다.
std::shared_ptr
와 달리,
unique_ptr
는
NullablePointer
를 만족하는 임의의 커스텀 핸들 타입을 통해 객체를 관리할 수 있습니다. 이는 예를 들어,
typedef
boost::offset_ptr
pointer;
를 정의하는
Deleter
를 제공하여 공유 메모리에 위치한 객체를 관리할 수 있게 합니다. 또는 다른
fancy pointer
를 사용할 수도 있습니다.
| 기능 테스트 매크로 | 값 | 표준 | 기능 |
|---|---|---|---|
__cpp_lib_constexpr_memory
|
202202L
|
(C++23) |
constexpr
std::unique_ptr
|
중첩 타입
| 유형 | 정의 |
| pointer |
std::
remove_reference
<
Deleter
>
::
type
::
pointer
해당 유형이 존재하는 경우, 그렇지 않으면
T*
.
NullablePointer
요구 사항을 충족해야 함
|
| element_type |
T
, 이
unique_ptr
가 관리하는 객체의 유형
|
| deleter_type |
Deleter
, 함수 객체 또는 함수나 함수 객체에 대한 lvalue 참조로, 소멸자에서 호출됨
|
멤버 함수
새로운
unique_ptr
을 생성합니다
(public member function) |
|
|
관리 객체가 존재할 경우 파괴합니다
(public member function) |
|
unique_ptr
를 할당합니다
(public member function) |
|
Modifiers |
|
|
관리 객체에 대한 포인터를 반환하고 소유권을 해제합니다
(public member function) |
|
|
관리 객체를 교체합니다
(public member function) |
|
|
관리 객체들을 교환합니다
(public member function) |
|
Observers |
|
|
관리 객체에 대한 포인터를 반환합니다
(public member function) |
|
|
관리 객체 파괴에 사용되는 삭제자를 반환합니다
(public member function) |
|
|
연관된 관리 객체가 있는지 확인합니다
(public member function) |
|
Single-object version,
|
|
|
관리 객체에 대한 포인터를 역참조합니다
(public member function) |
|
Array version,
|
|
|
관리 배열에 대한 인덱스 접근을 제공합니다
(public member function) |
|
비멤버 함수
|
(C++14)
(C++20)
|
새로운 객체를 관리하는 unique pointer를 생성합니다
(함수 템플릿) |
|
(C++20에서 제거됨)
(C++20)
|
다른
unique_ptr
또는
nullptr
와 비교합니다
(함수 템플릿) |
|
(C++20)
|
관리되는 포인터의 값을 출력 스트림에 출력합니다
(함수 템플릿) |
|
(C++11)
|
std::swap
알고리즘을 특수화합니다
(함수 템플릿) |
헬퍼 클래스
|
(C++11)
|
std::unique_ptr
에 대한 해시 지원
(클래스 템플릿 특수화) |
예제
#include <cassert> #include <cstdio> #include <fstream> #include <iostream> #include <locale> #include <memory> #include <stdexcept> // 아래 런타임 다형성 데모를 위한 헬퍼 클래스 struct B { virtual ~B() = default; virtual void bar() { std::cout << "B::bar\n"; } }; struct D : B { D() { std::cout << "D::D\n"; } ~D() { std::cout << "D::~D\n"; } void bar() override { std::cout << "D::bar\n"; } }; // unique_ptr를 소비하는 함수는 값으로 받거나 rvalue 참조로 받을 수 있습니다 std::unique_ptr<D> pass_through(std::unique_ptr<D> p) { p->bar(); return p; } // 아래 커스텀 삭제자 데모를 위한 헬퍼 함수 void close_file(std::FILE* fp) { std::fclose(fp); } // unique_ptr 기반 연결 리스트 데모 struct List { struct Node { int data; std::unique_ptr<Node> next; }; std::unique_ptr<Node> head; ~List() { // 리스트 노드를 루프에서 순차적으로 파괴, 기본 소멸자 // 재귀적으로 자신의 "next"의 소멸자를 호출했을 것입니다. // 충분히 큰 리스트에 대해 스택 오버플로우를 발생시킵니다. while (head) { auto next = std::move(head->next); head = std::move(next); } } void push(int data) { head = std::unique_ptr<Node>(new Node{data, std::move(head)}); } }; int main() { std::cout << "1) 고유 소유권 의미론 데모\n"; { // (고유 소유권을 가진) 리소스 생성 std::unique_ptr<D> p = std::make_unique<D>(); // 소유권을 "pass_through"로 이전합니다, // 이는 반환값을 통해 소유권을 다시 전달함 std::unique_ptr<D> q = pass_through(std::move(p)); // "p"는 이제 이동된 이후의 '빈' 상태이며, nullptr과 같습니다 assert(!p); } std::cout << "\n" "2) 런타임 다형성 데모\n"; { // 파생 리소스를 생성하고 기본 타입을 통해 이를 가리킵니다 std::unique_ptr<B> p = std::make_unique<D>(); // 동적 디스패치는 예상대로 작동합니다 p->bar(); } std::cout << "\n" "3) 커스텀 삭제자 데모\n"; std::ofstream("demo.txt") << 'x'; // 파일 읽기 준비 { using unique_file_t = std::unique_ptr<std::FILE, decltype(&close_file)>; unique_file_t fp(std::fopen("demo.txt", "r"), &close_file); if (fp) std::cout << char(std::fgetc(fp.get())) << '\n'; } // “close_file()”가 여기서 호출됨 (“fp”가 null이 아닌 경우) std::cout << "\n" "4) 사용자 정의 람다 표현식 삭제자 및 예외 안전성 데모\n"; try { std::unique_ptr<D, void(*)(D*)> p(new D, [](D* ptr) { std::cout << "커스텀 삭제자로부터 파괴 중...\n"; delete ptr; }); throw std::runtime_error(""); // "p"가 일반 포인터였다면 여기서 메모리 누수가 발생할 것임 } catch (const std::exception&) { std::cout << "예외 발생\n"; } std::cout << "\n" "5) unique_ptr 배열 형태 데모\n"; { std::unique_ptr<D[]> p(new D[3]); } // “D::~D()”는 3번 호출됨 std::cout << "\n" "6) 연결 리스트 데모\n"; { List wall; const int enough{1'000'000}; for (int beer = 0; beer != enough; ++beer) wall.push(beer); std::cout.imbue(std::locale("en_US.UTF-8")); std::cout << enough << " 벽에 있는 맥주 병...\n"; } // 모든 맥주를 파괴합니다 }
가능한 출력:
1) 고유 소유권 의미론 데모 D::D D::bar D::~D 2) 런타임 다형성 데모 D::D D::bar D::~D 3) 사용자 정의 삭제자 데모 x 4) 사용자 정의 람다 표현식 삭제자 및 예외 안전성 데모 D::D 사용자 정의 삭제자에서 파괴 중... D::~D 예외를 잡았음 5) unique_ptr 배열 형태 데모 D::D D::D D::D D::~D D::~D D::~D 6) 연결 리스트 데모 1,000,000 병의 맥주가 벽에...
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
| LWG 4144 | C++11 |
T*
가 유효한 타입을 형성해야 할 필요가 없었음
|
필요함 |
참고 항목
|
(C++11)
|
공유 객체 소유권 의미론을 가진 스마트 포인터
(클래스 템플릿) |
|
(C++11)
|
std::shared_ptr
이 관리하는 객체에 대한 약한 참조
(클래스 템플릿) |
|
(C++26)
|
값 의미론을 가진 동적으로 할당된 객체를 포함하는 래퍼
(클래스 템플릿) |
|
(C++17)
|
CopyConstructible
타입의 인스턴스를 보유하는 객체
(클래스) |