-
스마트 포인터란 Raw pointer를 감싸는 래퍼(Wrapper) 클래스 입니다.
기존의 포인터가 가진 몇 가지 문제점들을 회피하면서, 포인터의 기능을 제공합니다.
이번 글에서는 스마트 포인터의 한 종류인 std::unique_ptr<>에 대하여 살펴보도록 하겠습니다.
1. std::unique_ptr<>
더보기std::unique_ptr은 스마트 포인터를 사용할 때 가장 우선적으로 고려되는 포인터 입니다.
기본적으로 대부분의 연산에서 Raw pointer와 같은 명령을 수행합니다.
이것은 메모리 및 CPU의 사용에서 Raw pointer에 비해 무겁지 않음을 의미하고, Raw pointer를 사용할 때의 비용과 거의 동일하다는 뜻이 됩니다.
선언 및 사용은 아래와 같이 할 수 있습니다.
#include <iostream> #include <memory> int main int a = 10; int* b = &a; std::unique_ptr<int> c(new int(a)); std::cout << *b << "\n"; std::cout << *c << "\n"; }
10 10
이후 문단들에서 std::unique_ptr의 특징에 대하여 살펴보도록 하겠습니다.
2. 독점적 소유권
더보기std::unique_ptr은 포인팅 하는 대상에 대한 독점적 소유권(Exclusive ownership)을 가지고 있습니다.
NULL이 아닌 std::unique_ptr은 항상 자신이 가리키는 객체를 소유하고 있으며, 이 소유권은 다른 포인터와 공유되지 않습니다.
독점적인 소유권을 가져야 하기 때문에, std::unique_ptr은 복사 연산을 허용하지 않는 이동 전용 형식 입니다.
#include <iostream> #include <memory> int main(){ std::unique_ptr<Integer> a(new Integer(10)); std::unique_ptr<Integer> b = a; std::unique_ptr<Integer> c(a); }
C2280 : 'std::unique_ptr<>(&)': 삭제된 함수를 참조하려고 합니다.
위와 같이 복사 배정 연산자, 혹은 복사 생성자를 통해 std::unique_ptr을 생성하려 할 경우 컴파일이 실패합니다.
3. Delete, custom deleter
더보기std::unique_ptr은 소멸 시 자신이 가리키는 자원을 파괴합니다.
이동 연산의 경우, 이동 후 기존 std::unique_ptr은 nullptr로 설정됩니다.
std::unique_ptr의 파괴 작업은 내부적으로 가리키고 있는 Raw pointer에 delete연산을 적용하는 방식으로 수행됩니다.
이 때, delete연산 이외에 특정 작업이 필요할 수 있습니다.
이러한 경우를 위해 std::unique_ptr은 커스텀 삭제자를 사용할 수 있도록 할 수 있습니다.
#include <iostream> #include <memory> class Integer { public: Integer(int val) : data(val) { } virtual ~Integer(); private: int data; }; int main() { auto del_int = [](Integer* p_int) { std::cout << "Delete Integer\n"; delete p_int; }; std::unique_ptr<Integer, decltype(del_int)> a(new Integer(10), del_int); }
Delete Integer
커스텀 삭제자를 사용할 경우, std::unique_ptr에 그 형식과 해당 함수를 지정해야 합니다.
지정된 함수는 해당 객체가 소멸되는 시점에 호출됩니다.
4. std::unique_ptr<T[]>
더보기std::unique_ptr<>의 형태는 두 가지 입니다.
첫 번째는 개별 객체를 위한 std::unique_ptr<T>이고, 두 번째는 배열을 위한 std::unique_ptr<T[]>입니다.
지금은 비 권장 기능인 초기형태의 스마트 포인터인 std::auto_ptr<>은 배열의 해제에 관련된 이슈가 존재했는데, std::unique_ptr은 객체의 형태를 단일 객체와 배열로 세분화하면서 이러한 문제를 해결했습니다.
두 객체는 단일 객체인지, 배열인지의 차이가 존재하기 때문에 일부 특정 연산이 제한되어 있습니다.
예를 들어 단일 객체의 std::unique_ptr<T>은 operator[]를 제공하지 않으며, 배열 객체인 std::unique_ptr<T[]>은 역참조 연산 (operator*및 operator->를 제공하지 않습니다.
하지만 배열 형식으로 포인터를 관리하는 데 std::unique_ptr<T[]>는 권장되지 않습니다.
기본적으로 사용되는 템플릿이 내장 배열이라는 점에서, std::vector<>혹은 std::array<>등과 같은 STL의 배열이 나은 경우가 많기 때문입니다.
std::unique_ptr은 다른 스마트 포인터인 std::shared_ptr로의 유연한 변환이 가능합니다.
std::shared_ptr은 이름 그대로 공유가 가능한 스마트 포인터로, std::unique_ptr과는 비슷하지만 여러 부분에서 다른 특징을 가지고 있습니다.
이에 관해서는 다음 글에서 살펴보도록 하겠습니다.
감사합니다.
'C++ > Effective Modern C++' 카테고리의 다른 글
[Effective Modern C++] Smart pointer : std::weak_ptr<> (0) 2022.05.19 [Effective Modern C++] Smart pointer : std::shared_ptr<> (0) 2022.05.16 [Effective Modern C++] Special member function (0) 2022.05.05 [Effective Modern C++] const와 Thread safety (0) 2022.05.03 [Effective Modern C++] constexpr (0) 2022.04.29