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 |