728x90
반응형

우리는 동적 할당한 객체를 사용할 일이 더 이상 없을 때 해제해줘야 한다는 사실을 알고 있다.

아래의 예시를 보자.

void f()
{
	Investment *pInv = createInvestment();
	...
	delete pInv;
}

이렇게 하면 f를 벗어날 때 Investment객체를 해제하기 때문에 문제가 없어 보이지만, 중간에 예외가 발생하거나 return 되는 경우 등에 대한 고려는 없음을 알 수 있다.

위와 같은 경우를 막기 위한 방법으로는 자원을 객체에 넣고 그 자원 해제를 소멸자가 맡도록 하고, 소멸자는 실행 제어가 f를 떠날 때 호출되도록 만들면 되는데 이러한 내용을 템플릿 클래스로 만든 것이 스마트 포인터이다.

 

스마트 포인터

포인터처럼 동작하는 클래스 템플릿으로 사용이 끝난 메모리를 자동으로 해제해 준다.

책에는 auto_ptr과 shared_ptr이 나오지만 auto_ptr은 C++11부터 삭제되고 새로운 스마트 포인터들을 제공하므로, 여기서는 C++11 이후의 새로운 스마트 포인터들에 대해서 말하겠다.

  • unique_ptr
  • shared_ptr
  • weak_ptr

스마트 포인터는 이렇게 3종류로 memory 헤더 파일에 정의되어 있다.

 

1. unique_ptr

하나의 스마트 포인터만이 특정 객체를 소유할 수 있도록 객체에 소유권 개념을 도입한 스마트 포인터

이 스마트 포인터는 해당 객체의 소유권을 가지고 있을 때만, 소멸자가 해당 객체를 삭제할 수 있다.

그리고 unique_ptr 인스턴스는 move() 멤버 함수를 통해 소유권을 이전할 수는 있지만 복사할 수는 없으며, 소유권이 이전되면 이전 unique_ptr 인스턴스는 더는 해당 객체를 소유하지 않게 재설정된다.

unique_ptr<int> ptr01(new int(5)); // int형 unique_ptr인 ptr01을 선언하고 초기화함.
auto ptr02 = move(ptr01);          // ptr01에서 ptr02로 소유권을 이전함.

그리고 C++14 이후부터는 제공되는 make_unique() 함수를 사용하면 둘 이상의 unique_ptr가 한 객체를 참조하는 상황을 막아 인스턴스를 안전하게 생성할 수 있다.

// 한 객체를 둘 이상의 unique_ptr이 공유
Vecotr * vectorPtr = new Vector(10.f, 30.f);
std::unique_ptr<Vector> vector(vectorPtr);
std::unique_ptr<Vector> anotherVector(vectorPtr);
anotherVector = nullptr;


//make_pair를 통한 문제 해결
#include <memory>
#include "Vector.h"

int main()
{
  std::unique_ptr<Vector> myVector = std::make_unique<Vector>(10.f, 30.f);  
  myVector->Print();  
  return 0;
}

 

2. shared_ptr

하나의 특정 객체를 참조하는 스마트 포인터가 총 몇 개인지를 참조하여 참조 횟수가 0이 되면 메모리를 해제하는 스마트 포인터

동작 방식은 가비지 컬렉션과 비슷하나, 순환 참조를 이루는 경우(A가 B를, B가 A를 참조)를 없앨 수 없다는 점은 다르다.

shared_ptr<int> ptr01(new int(5)); // int형 shared_ptr인 ptr01을 선언하고 초기화함
cout << ptr01.use_count() << endl; // 1
auto ptr02(ptr01);                 // 복사 생성자를 이용한 초기화
cout << ptr01.use_count() << endl; // 2
auto ptr03 = ptr01;                // 대입을 통한 초기화
cout << ptr01.use_count() << endl; // 3

 

3. weak_ptr

하나 이상의 shared_ptr 인스턴스가 소유하는 객체에 대한 접근을 제공하지만, 소유자의 수에는 포함되지 않는 스마트 포인터

shared_ptr은 참조 횟수를 기반으로 동작하는 스마트 포인터인데, 만약 서로가 상대방을 가리키는 shared_ptr를 가지고 있다면, 참조 횟수는 절대 0이 되지 않으므로 메모리는 영원히 해제되지 않는다.

이렇게 서로가 상대방을 참조하고 있는 상황을 순환 참조라고 하는데 weak_ptr은 바로 이러한 shared_ptr 인스턴스 사이의 순환 참조를 제거하기 위해서 사용된다.

 

요약

  • 자원 누출을 막기 위해, 생성자 안에서 자원을 획득하고 소멸자에서 그것을 해제하는 RAII객체를 사용하자.
  • 스마트 포인터를 사용하자!
728x90
반응형