본문 바로가기

Language/C++

[C++] STL - 스마트 포인터(smart pointer)

728x90
반응형

스마트 포인터(smart pointer)

자바의 경우 garbage collector을 통해 메모리를 관리하지만 C++은 사용자가 스스로 메모리를 할당 해제를 통해 관리해야 한다. c에선는 malloc, free로 메모리를 할당 및 해제하고 C++은 new, delete를 사용한다. 이때 할당받은 메모리를 해제하지 않을 경우 프로그램은 계속 사용하고 있는 메모리로 인지하고 해당 메모리를 사용하지 않는 메모리 누수(memory leak)가 발생한다. 이와 같은 메모리 누수를 방지하기 위해 스마트 포인터를 제공한다. 스마트 포인터는 포인터처럼 사용하는 클래스 템플릿으로 메모리를 자동으로 해제해준다. 즉, delete를 자동으로 수행한다.

이런 SmartPtr 클래스를 사용하면 객체의 사용이 끝나면 자동으로 호출된다. 이곳에 delete가 존재해 메모리를 직접 해제하지 않아도 자동으로 해제된다. 따라서 이 클래스를 템플릿으로 일반화시켜 어떤 객체에 대해서도 메모리를 할당받은 경우에 자동으로 해제를 해주는 것이다.

 

즉, 스마트 포인터는 new 키워드를 사용해 일반 포인터가 실제 메모리를 가리키도록 초기화한 후 기본 포인터를 스마트 포인터에 대입하여 사용한다. 이렇게 정의된 스마트 포인터가 수명을 다하면 소멸자를 통해 delete 키워드를 자동으로 사용해 메모리를 해제한다. 따라서 따로 메모리를 해제할 필요가 없게 된다.

 

스마트 포인터 종류

스마트 포인터 기능은 C++ 11 이후부터 추가되었다. #include <memory> 해야 사용 가능하다.

① shared_ptr

shared_ptr은 어떤 하나의 객체를 참조하는 스마트 포인터의 개수를 참조하는 스마트 포인터이다. 이렇게 참조하고 있는 스마트 포인터의 개수를 참조 카운트(reference count)라고 한다. 참조 카운트란 해당 메모리를 참조하는 포인터가 몇 개인지 나타내는 값으로 shared_ptr가 추가될 때 1씩 증가하고 수명이 다하면 1씩 감소한다.

따라서 마지막 shared_ptr의 수명이 다하거나 main() 함수가 종료되면 참조 카운트가 0이 되어 delete를 사용하여 메모리를 자동으로 해제한다.

shared_ptr<객체> 스마트 포인터명(new 객체);
shared_ptr<객체> 스마트 포인터명 = make_shared<객체>(인수);

make_shared() 함수는 전달받은 인수를 사용하여 지정된 객체를 생성하고 생성된 객체를 참조하는 shared_ptr을 반환하기 때문에 해당 함수를 사용하면 shared_ptr 객체를 예외발생에 대해 안전하게 생성할 수 있다.

use_count() 멤버 함수는 shared_ptr 객체가 현재 가리키고 있는 객체를 참조 중인 소유자의 수를 반환해주기 때문에 reference count와 동일한 값을 가진다.

shared_ptr s는 5라는 값을 가지는 int형 객체를 가리키게 하였다. 아직은 스마트포인터 s 1개만 해당 객체를 가리키고 있으므로 reference count는 1이 출력되게 된다.

다음 s2가 s가 가리키는 객체와 같은 값을 가리키는 shared_ptr이 되도록 auto를 사용하였다. 그리고 5라는 값을 가지는 객체의 값을 7로 증가시키고 확인해보면 이제 7이란 객체를 s와 s2가 가리키므로 reference count는 2가된다.

마지막으로 s3를 s2가 가리키는 객체를 가리키도록 하였다. 그럼 s, s2, s3 총 3개의 스마트포인터가 7이란 객체를 가리키고 있으므로 reference count가 3이 나오는것을 확인할 수 있다.

즉, shared_ptr은 객체가 가진 값의 변경 여부는 관계없이 객체를 가리키는 스마트 포인터의 개수를 참조하는 것을 확인할 수 있다. 

 

② unique_ptr

unique_ptr은 하나의 스마트 포인터만이 객체를 가리킬 수 있도록 한다. 즉 shared_ptr과 다르게 reference count가 1을 넘길수 없다.

unique_ptr<객체> 스마트 포인터명(new 객체)
unique_ptr<객체> 스마트 포인터명 = make_unique<객체>(인수);

make_shared()함수 처럼 make_unique()함수를 사용하여 안전하게 인스턴스를 생성할 수 있다.

따라서 위와같이 동일한 객체를 다른 스마트 포인터 객체로 참조하려고 하면 에러가 발생한다.

단 하나의 스마트 포인터만 특정 객체를 가리킬수 있으므로 다른 스마트 포인터를 사용하고 싶다면 move()함수를 사용하여 가리키는 객체를 변경해야한다.

 

③ weak_ptr

weak_ptr은 하나 이상의 shared_ptr가 가리키는 객체를 참조할수 있지만 reference count를 늘리지않는 스마트 포인터이다. shared_ptr을 사용할때 발생할 수 있는 문제를 해결하기 위해 사용된다.

shared_ptr은 하나의 객체를 여러 스마트 포인터가 가리키고 reference count를 통해 동작한다. 그런데 만약 서로가 서로를 가리키는 shared_ptr을 가지게 되면 reference count가 0이 될 수가 없으므로 메모리가 해제되지 않는 순환 참조(circular reference)가 발생하게 된다. weak_ptr은 이러한 순환 참조를 제거하기 위해 사용된다.

 

728x90
반응형