Programming/C&CPP

스마트하지 못한 스마트한 포인터 auto_ptr

psychoria 2014. 12. 8. 20:50
반응형

C/C++은 메모리 관리가 까다로운 언어입니다.

개발자가 직접 메모리를 할당하고 해제해야 할 책임이 있습니다.

C++의 STL에는 auto_ptr이라는 스마트 포인터가 존재합니다.

다만 auto_ptr은 C++11 이후에는 사라졌기 때문에 사용이 불가능합니다.

auto_ptr은 <memory> 헤더에 구현되어 있습니다.

auto_ptr이 스마트하지 못한 이유를 확인해 보겠습니다.

auto_ptr은 템플릿 기반이기 때문에 어떤 타입의 포인터든지 받을 수 있습니다.

클래스의 큰 특징은 객체가 지정된 범위(Scope)를 벗어나면 소멸자가 호출된다는 점입니다.

이러한 특징을 통해서 클래스는 객체가 사라지기 전에 정리하거나 해제해야 할 것을 소멸자에서 호출합니다.

메모리를 할당하고 해제하지 않으면 메모리 누수(Memory Leak)가 발생합니다.

#include <iostream>
using namespace std;

int main()
{
	int* pInt = new int;

	*pInt = 10;
	cout << *pInt << endl;

	// delete pInt;

	return 0;
}

new로 동적 할당을 했지만 delete를 호출하지 않았기 때문에 메모리 누수가 발생하는 것입니다.

이럴 때 사용할 수 있는 것이 auto_ptr입니다.

#include <iostream>
#include <memory>
using namespace std;

int main()
{
	auto_ptr pVal(new int);
	*pVal = 10;

	cout << *pVal << endl;

	return 0;
}

객체를 생성하는 부분이 조금 달라졌고 사용하는 것은 동일합니다.

auto_ptr<타입> 형태로 사용하면 됩니다.

초기화는 new int를 통해서 새로 할당한 메모리의 주소의 전달했습니다.

auto_ptr은 delete를 사용하지 않는데 auto_ptr의 소멸자는 다음과 같이 구현되어 있습니다.

~auto_ptr()
{
	delete _Myptr;
}

_Myptr은 auto_ptr이 내부적으로 관리하는 포인터입니다.

바로 초기화에서 new int를 통해서 전달한 주소를 가리키는 포인터입니다.

할당된 주소를 전달받아서 내부적으로 관리하고 소멸자가 호출될 때 delete를 호출하는 것입니다.

소멸자에서 delete를 사용하기 때문에 auto_ptr의 한 가지 단점이 나옵니다.

new로 생성한 단일 객체에 대해서만 메모리의 해제를 보장한다는 것입니다.

malloc 등으로 할당할 경우에는 free가 호출되지 않기 때문에 해제가 안되는 것입니다.

또한 new[]로 메모리를 할당할 경우 메모리가 정상적으로 해제되지 않을 수 있습니다.

관련된 내용은 아래 링크에서 확인이 가능합니다.

2014/12/06 - [Programming/C&C++] - C/C++ 포인터 기본

auto_ptr 이후의 스마트 포인터들은 functor(함수 객체)를 통해서 해제 함수를 전달 받아서 해결합니다.

C++11 이후에는 람다(Lamdba)를 통해서도 구현이 가능합니다.

대입을 하면 복사가 되지 않고 대입 이후에 기존의 auto_ptr은 NULL로 변경됩니다.

a = b;와 같은 연산을 통해서 보통 a와 b는 같은 값을 갖게 되지만 auto_ptr은 b가 초기화 됩니다.

이렇게 동작하는 이유는 한 객체가 소멸자에서 먼저 해제했는데

다음 객체도 소멸자에서 해제를 시도하다가 문제가 생길 수 있기 때문입니다.

이 점이 auto_ptr의 두 번째 단점입니다.

동일한 메모리 위치를 가리키는 객체를 2개 이상 생성하지 않아야 한다는 점입니다.

물론 억지로 가리키게 할 수는 있지만 해제 과정에서 문제가 생깁니다.

auto_ptr의 release() 메소드는 포인터를 돌려주고 자신을 NULL로 바꾸는 기능을 합니다.

이런 식으로 주소를 돌려 받을 수 있습니다.

주소를 돌려 받게 되면 해제는 직접 해야합니다.

동일한 메모리 위치를 가리키는 객체를 여러 개 만들기 위해서 shared_ptr 등의 경우에는

참조 카운트(Reference count)를 통해서 해결합니다.

참조 카운트를 유지하면서 해당 메모리를 참조하는 부분의 수를 기억하고 있는 것입니다.

이후에 참조하는 부분이 하나도 없어서 참조 카운트가 0이 될 때 메모리를 해제하는 것입니다.

마지막으로 C++11 이후에는 auto_ptr은 사용하지 않습니다.

C++11이 지원되는 환경이라면 auto_ptr을 완전히 대체하는 unique_ptr을 사용하면 됩니다.

반응형