- 자원을 직접 접근하는 경우가 있기 때문에 RAII 클래스를 만들 때는 관리하는 자원을 얻을 수 있는 방법을 구현해야 함
- 자원 접근은 명시적 변환, 암시적 변환을 통해 가능, 안전성만 따지면 명시적 변환이 대체적으로 더 나음
RAII 객체에서 자원을 변환할 방법
명시적 변환
스마트 포인터의 get 함수 : 실제 포인터의 사본을 얻음
스마트 포인터의 operator->, operator*
암시적 변환
명시적 변환 함수를 호출하지 않아도 되므로 편리하지만 실수 발생할 가능성이 높음
f를 의도하지 않게 f2도 공유하여 한쪽에서 소멸시킬 경우 다른 한 쪽에서는 소멸된 객체를 가리킬 수 있는 위험이 있음
class FontHandle;
void changeFontSize(FontHandle f);
class Font
{
public:
operator FontHandle () const { return f; } // 암시적 변환 함수
private:
FontHandle f; // 실제 폰트 자원
};
Font f(getFont());
changeFontSize(f); // Font->FontHandle로 암시적 변환 수행
FontHandle f2 = f; // Font 객체를 복사하려고 했는데 f가 FontHandle로 변경되고 복사됨
- RAII 객체의 복사는 그 객체가 관리하는 자원의 복사 문제를 안고 가기 때문에, 그 자원을 어떻게 복사하느냐에 따라 RAII 객체의 복사 동작이 결정됨
- RAII 클래스의 일반적인 복사 동작은 복사를 금지하거나 참조 카운팅을 해주는 선으로 마무리하자, 이외의 방법들도 가능
힙에서 생기지 않는 자원은 스마트 포인터로 처리하기에 적절하지 않으므로 힙에서 생기지 않는 자원들은 자원 관리 클래스를 만들어야 함
// Outer를 설정하여 컴포지션 관계 설정 및 객체의 생명 주기를 Outer와 동일하게
CourseInfo = NewObject<UCourseInfo>(this);
// 객체의 생명 주기를 유지할 필요가 없기 때문에 Outer 설정 X
UStudent* Student1 = NewObject<UStudent>();
// AddUObject를 사용하여 오브젝트의 멤버 함수를 바인딩
CourseInfo->OnChanged.AddUObject(Student1, &UStudent::GetNotification);
예제를 통해 실습한 결과 Delegate 호출할 때 바인딩이 늦게 된 순서대로 호출되는 것으로 보임
- 자원 누출을 막기 위해, 생성자 안에서 자원을 획득하고 소멸자에서 그것을 해제하는 RAII 객체를 사용하자
- 일반적으로 널리 쓰이는 RAII 클래스는 unique_ptr, shared_ptr
unique_ptr은 복사될 때 소유권이 이동, shared_ptr은 복사될 때 소유권을 공유
파생 클래스의 객체를 얻는 팩토리 함수를 사용했을 때 로우 포인터를 반환하면 사용자가 직접 소멸시켜야 함
- 객체 복사 함수는 주어진 객체의 모든 데이터 멤버 및 모든 기본 클래스 부분을 빠뜨리지 말고 복사해야 함
- 클래스의 복사 함수 두 개를 구현할 때, 한쪽을 이용해서 다른 쪽을 구현하려는 시도는 절대로 하지 말자
대신 공통된 동작을 제 3의 함수에 분리하고 양쪽에서 이것을 호출하자
복사 함수
복사 생성자, 복사 대입 연산자
복사 함수에 멤버 복사를 빠뜨렸을 경우 컴파일러가 알려주지 않으므로 멤버를 추가하면 복사 함수에 추가해야 함