스토리텔링 개발자

[Effective C++] 13. 자원 관리 객체(RAII) 본문

개발/Effective C++

[Effective C++] 13. 자원 관리 객체(RAII)

김디트 2024. 6. 3. 12:42
728x90

항목 13 : 자원 관리에는 객체가 그만!

 

 

 

new / delete 문의 짝을 맞추지 못하게 되는 상황
class Investment { ... };

Investment* createInvestment();

void f()
{
    Investment* pInv = createInvestment();
    
    ... // pInv 사용부.
    // 하지만 이 도중에 아래로 가지 않고 리턴해버릴 여지가 있다!!
    
    delete pInv; // 반드시 불러줘야 객체가 해제된다.
}
  • 객체 삭제는 실패할 수 있는 경우가 다양하다.
    1. 삭제 전에 return문이 들어있는 경우
    2. 삭제 전 continue 혹은 goto로 루프를 갑작스래 빠져나왔을 경우
    3. 사용 중에 예외가 발생할 경우

 

 

 

스마트 포인터
void f()
{
    auto_ptr<Investment> pInv(createInvestment()); // 팩토리 함수 호출
    ... // auto_ptr을 사용했으므로 delete 문 불필요
}
  • 자원을 객체에 넣어 자원 해제를 해당 객체의 소멸자가 맡도록 한다.
    • 즉, 자원 관리 객체
  • 객체의 소멸자가 유효 범위를 벗어날 시 호출되도록 한다.

 

 

 

자원 관리 객체의 특징
  • 자원 획득 즉 초기화 (Resource Acquisition Is Initialization : RAII)
    • 자원을 획득함과 동시에 자원관리 객체에게 넘긴다.
    • 자원 획득, 자원 관리 객체의 초기화, 이 두 가지가 한 문장인 것이 너무나 일상적이기 때문에 생긴 단어이다.
// 한 문장에 두 가지가 한번에 이루어진다.
auto_ptr<Investment> pInv(createInvestment());
  • 자원 관리 객체는 자신의 소멸자를 사용해서 자원이 확실히 해제되도록 한다.

 

 

 

auto_ptr 의 문제점
  • auto_ptr 은 자신이 소멸될 때 관리 대상을 자동으로 delete 시킨다.
  • 그래서 하나의 자원에 대한 auto_ptr 의 개수는 둘 이상일 수 없다.
    • 둘 중 하나의 auto_ptr이 소멸되면, 자원이 삭제되어 버린다!
  • auto_ptr 객체를 복사하면 원본 객체를 null로 만든다.
auto_ptr<Investment> pInv1(createInvestment());

auto_ptr<Investment> pInv2(pInv1); // 복사 동작. pInv1은 이제 null이다.

pInv1 = pInv2; // 복사 동작. pInv2는 이제 null이다.

 

 

 

참조 카운팅 방식 스마트 포인터 (RCSP), shared_ptr
  • 레퍼런스 카운터 계산으로 자원을 관리한다.
  • 다만 순환 참조 문제가 발생할 수 있다.
void f()
{
    ...
    shared_ptr<Investment> pInv1(createInvestment()); // auto_ptr과 동일하게 사용 가능
    shared_ptr<Investment> pInv2(pInv1); // 복사. pInv1, pInv2가 동시에 동일객체를 가리킨다.
    
    pInv1 = pInv2; // 복사. 허나 변하는 것은 없다.
    
    ... // 역시 delete 문은 불필요하다.
}

 

 

 

주의점
  • auto_ptr, shared_ptr은 내부 소멸자에서 delete[]가 아닌 delete 연산자를 사용한다.
    • 즉, 동적 할당 배열에는 사용할 수 없다.
    • 그러나 사용해도 컴파일 에러가 발생하지 않는다.
auto_ptr<string> aps(new string[10]); // 객체 해제 시 delete[]를 사용하지 않는다!

shared_ptr<int> spi(new int[1024]); // 위와 동일한 문제가 발생한다!
  • 위의 경우처럼 auto_ptr, shared_ptr 로도 제대로 관리할 수 없는 자원의 경우라면?
    • 자원 관리 클래스를 직접 만들 수 밖에 없다. (항목 14, 항목 15 참조)
  • 팩토리 메소드의 반환 타입이 포인터면?
    • 호출자 쪽에서 필수적으로 delete 호출을 해주어야 한다.
    • 혹은 위의 예제처럼 팩토리 메소드를 호출함과 동시에 스마트 포인터로 감싸줘야 한다.
    • 하지만 애초에 메소드가 스마트 포인터를 반환하면 이 부분을 개선 할 수 있다. (항목 18 참조)
728x90
Comments