C++11에서 auto_ptr이 사라지고 unique_ptr이 새로 추가되었습니다.
auto_ptr의 문제점에 대해서는 아래의 글을 확인하시면 됩니다.
2014/12/08 - [Programming/C&C++] - 스마트하지 못한 스마트한 포인터 auto_ptr
auto_ptr을 대체하는 unique_ptr에 대해서 알아보도록 하겠습니다.
unique_ptr은 auto_ptr과 거의 유사한 멤버를 가지고 있습니다.
동적 할당된 포인터를 받아서 해당 포인터를 핸들링하고 자동으로 메모리를 해제하는 역할까지 동일합니다.
* 연산자나 -> 연산자도 auto_ptr과 동일하게 지원합니다.
unique_ptr을 생성하는 코드를 보도록 하겠습니다.
#include <string> #include <memory> #include <iostream> struct TestStuff { TestStuff() : nIntVal(0), szVal(){}; TestStuff(int nValue, std::string szValue) : nIntVal(nValue), szVal(szValue){}; int nIntVal; std::string szVal; }; int main() { // Make unique_ptr variable std::unique_ptr<TestStuff> ptr_Temp1(new TestStuff{ 3, "Hello" }); std::cout << ptr_Temp1->nIntVal << " and " << ptr_Temp1->szVal << std::endl; // Make unique_ptr variable using std::make_unique std::unique_ptr<TestStuff> ptr_Temp2 = std::make_unique<TestStuff>(4, "Hi"); std::cout << (*ptr_Temp2).nIntVal << " and " << (*ptr_Temp2).szVal << std::endl; return 0; }
std::unique_ptr<T> 의 형식으로 변수를 선언하고 new로 메모리를 할당해서 생성이 가능합니다.
또한 C++ 14에서 새로 추가된 std::make_unique()를 사용해서 생성도 가능합니다.
물론 C++ 14를 지원하는 컴파일러로 작업을 해야 합니다.
ptr_Temp1과 ptr_Temp2가 생성이 됩니다.
unique_ptr을 사용하는 방법은 다음과 같습니다.
int main() { // Make unique_ptr variable std::unique_ptr<TestStuff> ptr_Temp1(new TestStuff{ 3, "Hello" }); std::cout << ptr_Temp1->nIntVal << " and " << ptr_Temp1->szVal << std::endl; // Make unique_ptr variable using std::make_unique std::unique_ptr<TestStuff> ptr_Temp2 = std::make_unique<TestStuff>(4, "Hi"); std::cout << (*ptr_Temp2).nIntVal << " and " << (*ptr_Temp2).szVal << std::endl; ptr_Temp1.reset(); // ptr_Temp1 is nullptr. if (ptr_Temp1) // operator bool std::cout << ptr_Temp1->nIntVal << " and " << ptr_Temp1->szVal << std::endl; else std::cout << "Empty" << std::endl; ptr_Temp1.reset(new TestStuff{ 5, "Bye" }); // ptr_Temp1 is assigned. TestStuff* pStuff = ptr_Temp2.release(); // ptr_Temp2 is nullptr. if (ptr_Temp2) std::cout << ptr_Temp2->nIntVal << " and " << ptr_Temp2->szVal << std::endl; else std::cout << "Empty" << std::endl; delete pStuff; // must delete assigned memory. // Must use std::move() for move pointer. ptr_Temp2 = std::move(ptr_Temp1); // ptr_Temp1 is nullptr, ptr_Temp2 is 5, "Bye" if (ptr_Temp2) std::cout << ptr_Temp2->nIntVal << " and " << ptr_Temp2->szVal << std::endl; else std::cout << "Empty" << std::endl; return 0; }
reset()은 내부에 있는 포인터의 메모리를 해제하고 nullptr로 변경합니다.
reset()을 사용할 때 reset(new TestStuff{...}) 등으로 사용하면 새로운 메모리 할당이 가능합니다.
또한 unique_ptr은 그 자체로 bool연산이 가능해서 nullptr일 경우 false를 리턴합니다.
또한 release() 멤버 함수를 사용해서 unique_ptr 내부의 포인터를 다른 포인터로 옮기는 것도 가능합니다.
이 때는 반드시 받은 포인터의 메모리를 해제해 줘야 합니다.
또한 = 연산자로 auto_ptr과 동일하게 포인터를 옮길 수 있지만 std::move()를 사용해서 옮겨야 합니다.
아래와 같은 방법을 사용하면 동적 할당된 배열에 대해서도 처리가 가능합니다.
std::unique_ptr<int[]> ptr(new int[4]{1, 2, 3, 4});
배열에 대한 deleter가 구현이 되어 있기 때문에 int[] 등으로 사용하게 되면 내부적으로는 delete[]가 호출이 됩니다.
마지막으로 unique_ptr은 아래와 같이 deleter를 직접 구현하는 것도 가능합니다.
struct ArrayDeleter { template <class T> void operator()(T* p) { delete[] p; } }; int main() { std::unique_ptr<int[], ArrayDeleter> ptr(new int[4]{1, 2, 3, 4}); }
STL의 functor를 활용해서 delete를 직접 구현하고 변수 선언할 때 넣어줄 수 있습니다.
이렇게 하면 메모리를 동적 할당하는 방법에 제약이 생기지 않습니다.
또한, functor를 활용하면 내부에서 추가적으로 할 동작을 정의할 수 있는 장점이 있습니다.
메모리 누수의 걱정이 생길 때는 unique_ptr를 활용해서 개발을 하시면 큰 도움이 될 것입니다.
unique_ptr의 설명을 마치겠습니다.
'Programming > CPP11&14' 카테고리의 다른 글
[C++11] static_assert를 통한 컴파일 타임 검증 (0) | 2015.02.06 |
---|---|
[C++11] default와 delete 키워드 (0) | 2015.01.08 |
[C++11] cbegin()과 cend(), crbegin()과 crend() (0) | 2014.12.24 |
[C++11] final과 override (0) | 2014.12.21 |
[C++11] RValue Reference(4) (0) | 2014.12.18 |