본문 바로가기
C,C++/C++

array를 소유하는 std::shared_ptr, deleter 설정법

by woohyeon 2021. 2. 25.
반응형

C++ 17부터 std::shared_ptr가 [] 연산자를 지원한다. 따라서 ssptr[i]와 같은 연산을 지원한다.

이 의미는 (ssptr.get())[i]와 같은 의미이다. 즉 int* p = new int[5]에서 p[i]와 동일하다.

그러려면 std::shared_ptr<int[]> ssptr(new int[5], deleter) 와같이 선언한다. 소유하려는 타입이 array일 경우 deleter를 지정하지 않아도 default_deleter가 자동으로 설정된다. deleter는 나중에 소유한 array의 메모리를 해제해준다. 

deleter를 직접 작성할 순 있지만 형태는 정해져 있는 듯 하다.

template <typename T>
struct array_deleter
{
	void operator()(T const* p)
	{
		delete[] p;
	}
};

위와 같이 선언했을 경우 std::shared_ptr<int[]> ssptr(new int[5], array_deleter<int>()); 와 같이 선언한다.

나는 포인터를 저장하는 배열이 필요해서 시도해봤더니 잘 되는 것 같다. 이 경우 배열의 크기만큼 delete를 해주어야 해서 size가 필요한데, deleter에 추가로 인자를 전달하지 못하는 것 같다. 그래서 현재 내 방법엔 미리 크기를 알고 있는 경우만 해당된다.  

내가 하려는 것을 로우 포인터 타입으로 보면 다음과 같다.

// 포인터를 저장하는 배열 생성
int** pArray = new int* [5];
for(size_t i = 0; i < 5; ++i)
{
	pArray[i] = new int(i);
}

...

// 메모리 해제
for(size_t i = 0; i < 5; ++i)
{
	delete pArray[i];
}

delete[] pArray;

 위와 같은 동작을 shared_ptr로 하려면 다음과 같이 한다.

template <typename T>
struct array_deleter
{
	void operator()(T const* p)
	{
		for (size_t i = 0; i < 5; ++i)
		{
			delete p[i];
		}

		delete[] p;
	}
};

std::shared_ptr<int*[]> sptr(new int* [5], array_deleter<int*>());

// 0 1 2 3 4
for(size_t i = 0; i < 5; ++i)
{
	sptr[i] = new int(i);
	cout << *sptr[i] << " "; 
}

sptr이 자신의 스코프를 벗어나면 deleter가 호출되며 배열의 각 요소에 저장된 포인터를 해제하고 마지막으로 배열 메모리까지 정상적으로 해제한다.




댓글