항목 13. 자원 관리에는 객체가 그만!
팩토리 메서드를 미리 숙지하고 13장을 살펴보자.
1. 팩토리 패턴
class Investment() { // 투자 관련 최상위 클래스
… };
class InvestmentA : Investment() { //파생클래스
… };
class Creator {
public :
Investment* Operation() { return factoryMethod(); }
protected :
virtual Investment* factoryMethod() = 0 ;
};
class CreatorA : Creator{
private:
Investment* factoryMethod() { return new InvestmentA }; // 투자 파생클래스 객체를 동적할당하고 그 포인터를 반환한다. 따라서 객체의 해제는 호출자 쪽에서 직접 해야합니다.
};
위 코드는 일반적인 팩토리 패턴의 예이다.
void f()
{
InvestmentA *pInv = CreatorA.fatoryMethod(); // 팩토리 함수 호출
… //??!
delete pInv; // 객체 해제
}
void f()함수는 제대로 된 코드로 보이지만, 객체 삭제의 실패할 수 있는 가능성이 높다.
(1) "…" 구간에서 return
(2) 생성과 해제가 같은 루프일때, continue goto가 섞여있을경우
(3) "…"에서 예외발생
f()가 객체를 제대로 해제 해줄거라고 믿으면 안된다.
2. 해결방법
- 자원을 객체에 넣고 자원해제를 소멸자가 맡게한다. 그 소멸자는 실행제어가 f()를 떠날때 호출되도록 만들어야 한다.
이 아이디어는 이미 표준라이브러리에 만들어져 있다.
(1) auto_ptr (스마트 포인터)
(2) tr1::shared_ptr
(1) 스마트포인터를 활용하여 f()를 다시 작성해보자.
void f()
{
std::auto_ptr<InvestmentA> pInv(CreatorA.fatoryMethod()); //동일
… //pInv 사용
} //auto_ptr의 소멸자를 통해 pInv를 삭제한다.
3. 자원관리 두가지 특징
(1) 자원 획득후 자원 관리 객체에게 넘긴다.
- Resource Acquisition Is Initialization (RAII : 자원 획득 즉 초기화)
(2) 자원 관리 객체는 소멸자를 사용해서 자원이 확실히 해제하도록 한다.
auto_ptr은 자신이 소멸될때 자신이 가리키는 객체를 삭제하기 때문에, 그 객체를 가리키는 auto_ptr이 2개이상이면 절대로 안되겠다. 그리고 auto_ptr은 auto_ptr객체를 복사대입하면 기존 auto_ptr의 데이터를 NULL로 만든다.
STL 컨테이너 같은경우는 auto_ptr과 성질이 상반되기 때문에 위에 기재한 tr1:;shared_ptr을 사용한다.
그 이유는 여기를 참고해보자.
이것만은 잊지말자!
(1) 자원 누출을 막기위해 생성자 안에서 자원을 획득하고, 소멸자에서 그것을 삭제하는 RAII 객체를 사용하자.
(2) 자주 쓰이는 RAII 클래스는 auto_ptr과 tr1::shared_ptr이다. 이 둘 가운데 tr1::shared_ptr이 복사 시 동작이 직관적이기 때문에 이 놈이 대개 더 좋습니다.