Effective C++/More Effective C++
[More Effective C++] 9. 자원 관리 객체(RAII)
김디트
2024. 8. 9. 10:46
728x90
항목 9 : 리소스 누수를 피하는 방법의 정공은 소멸자이다.
지역 리소스에 대한 포인터
- 지역 리소스(스택에서 생긴 리소스)를 조작할 때, 포인터는 부적절할 수 있다.
- 소멸자가 불리지 않는 상황이 발생할 수 있기 때문이다.
- 즉, 리소스 누수가 발생할 수 있다는 뜻이다.
class ALA
{
public:
virtual void processAdoption() = 0;
...
};
class Puppy : public ALA
{
public:
virtual void processAdoption();
...
};
class Kitten : public ALA
{
public:
virtual void processAdoption();
...
};
// s로부터 동물 정보를 읽어서
// 적절한 타입의 객체를 동적할당 후
// 포인터 반환
ALA* readALA(istream& s) { ... }
void processAdoptions(istream& dataSource)
{
while(dataSource)
{
ALA* pa = readALA(dataSource); // 객체 생성
pa->processAdoption(); // 가상 함수 처리
delete pa; // 객체 삭제
}
}
- 헌데 위의 로직에서 '가상 함수 처리'(pa->processAdoption()) 중 예외가 발생하면?
- delete가 불리지 않으면서 리소스 누수가 발생한다.
- 예외를 처리
void processAdoptions(istream& dataSource)
{
while(dataSource)
{
ALA* pa = readALA(dataSource);
try
{
pa->processAdoption();
}
catch(...)
{
delete pa; // 객체 삭제
throw; // 예외 전파
}
delete pa; // 객체 삭제
}
}
- 문제점
- try - catch 블록으로 코드가 복잡해진다.
- 코드 중복이 발생한다.( delete pa )
예외 발생 시 소멸자를 반드시 호출하는 방법
- 포인터 대신 포인터처럼 동작하는 객체, 즉 스마트 포인터를 사용한다.(항목 28 참조)
- 자신의 유효범위(scope)를 벗어나면 자신이 가리키는 메모리를 삭제한다.
template<typename T>
class auto_ptr
{
public:
auto_ptr(T* p = 0) : ptr(p) {}
~auto_ptr() { delete ptr; }
private:
T* ptr;
};
void processAdoptions(istream& dataSource)
{
while(dataSource)
{
auto_ptr<ALA> pa(readALA(dataSource)); // 스마트 포인터 사용으로 대체
pa->processAdoption();
]
}
- 다만 이는 단일 객체에 대해서만 유효하며, 배열 포인터의 경우 다른 방도가 필요하다.
- 스마트 포인터가 단일 객체 형태의 delete를 사용하기 때문이다. (항목 8 참조)
- 배열 포인터에 대해 auto_ptr처럼 동작하는 클래스가 필요하다면 만들어야 한다.
- 하지만 vector 같은 STL 컨테이너를 쓰는 것이 훨씬 나은 선택이다.
자원 관리 객체를 사용하는 다른 예
void displayInfo(const Information& info)
{
WINDOW_HANDLE w(createWindow()); // 윈도우 리소스 획득
w를 핸들로 하는 윈도우에 정보를 표시한다;
destroyWindow(w); // 윈도우 리소스 해제
}
- 리소스가 포인터는 아니지만, 윈도우 리소스가 해제되지 않으면 리소스 누수가 발생한다.
- 자원 관리 객체로 자원 획득 / 해제를 컨트롤하도록 수정한다.
class WindowHandle
{
public:
WindowHandle(WINDOW_HANDLE handle) : w(handle) {}
~WindowHandle() { destroyWindow(w); }
operator WINDOW_HANDLE() { return w; } // WINDOW_HANDLE로의 암시적 변환 지원
private:
WINDOW_HANDLE w;
// 복사 생성자 자동 생성 막기(항목 28 참조)
WindowHandle(const WindowHandle&);
WindowHandle& oepratr=(const WindowHandle&);
};
void displayInfo(const Information& info)
{
WindowHandle w(createWindow());
w를 핸들로 하는 윈도우에 정보를 표시한다;
}
- 암시적 변환 지원을 하고 있으나, 일반적으로는 좋지 않은 생각이다.(항목 5 참조)
- 복사 동작을 막아뒀다.(EC++ 항목 14 참조)
참고
[Effective C++] 13. 자원 관리 객체(RAII)
항목 13 : 자원 관리에는 객체가 그만! new / delete 문의 짝을 맞추지 못하게 되는 상황class Investment { ... };Investment* createInvestment();void f(){ Investment* pInv = createInvestment(); ... // pInv 사용부. // 하지만
delightlane.tistory.com
728x90