Item 14: 자원 관리 클래스를 복사할 때 주의하자

1. RAII를 이용하여 Mutex를 관리하는 간단한 예를 들어보자.
void lock(Mutex *pm);   // 뮤텍스에 대한 권한을 얻어온다.
void unlock(Mutex *pm); // 뮤텍스에대한 권한을 반환한다.

// RAII class
class Lock {
public:
    explicit Lock(Mutex *pm)
    : mutexPtr(pm)
    {
        lock(mutexPtr);         // 뮤텍스에 대한 권한을 얻어온다
    }

    ~Lock(Mutex *pm)
    {
        unlock(mutexPtr);       // 뮤텍스에 대한 권한을 반환한다
    }
private:
    Mutex *mutexPtr;
};

사용할 때는 아래처럼 사용한다.

Mutex m;       // 사용할 뮤텍스를 정의한다
...
{              // 임계영역을 정하기 위해 블록을 만든다
    Lock(&m);  // 뮤텍스에 잠금을 설정한다
    ...        // 임계영역에서 작업을 한다
}              // 블록의 끝. 뮤텍스에 걸렸던 잠금이 자동으로
               // 해제된다.
2. 만약 lock 객체가 아래처럼 복사된다면 어떻게 해야 하나?
Lock ml1(&m);  // m에 잠금을 건다
Lock ml2(ml1); // ml1을 ml2에 복사한다.
               // 어떻게 동작해야 하나?

경우에 따라 다른 동작을 해야 한다.

  • 복사를 금지한다

실제로 RAII 객체를 복사하면 안되는 경우가 가장 많다.

이를 막기 위해서 복사함수들을 private으로 만들면 된다. Item 6 참조.

class Lock: public Uncopyable {   // 복사를 금지한다.
public:                           // 항목 6을 참조하자.
    ...                           // 나머지는 이전과 같다.
};
  • 관리하고 있는 객체에 대한 참조카운팅을 수행한다.

자원을 사용하고 있는 마지막 객체가 소멸될 때까지 자원을 반환하지 않아야 하는 경우에 사용하자.

대표적인 예가 shared_ptr이다.

shared_ptr은 참조 카운트가 0이 될때 자신이 가리키고 있는 대상을 버리도록 되어 있다.

위의 예에서는 Mutex를 지우는 것이 아니라 락만 해제해야 한다. 다행히 shared_ptr은 사용자 정의 삭제자를 지원한다. 아래 처럼 사용이 가능하다.

class Lock {
public:
    explicit Lock(Mutex *pm)    // shared_ptr을 초기화하는데, 가리킬 포인터로
    : mutexPtr(pm, unlock)      // Mutex 객체의 포인터를 사용하고 삭제자로
    {                           // unlock 함수를 지정한다.
        lock(mutexPtr.get());
    }
private:
    std::tr1::shared_ptr<Mutex> mutexPtr;
};

Lock 클래스에 소멸자가 없음에 유의하라.

  • 컴파일러가 자동으로 만드는 소멸자는 비정적 데이터 멤버에 대한 소멸자를 자동으로 호출해준다. 여기에서는 mutexPtr이 해당된다.
  • mutexPtr의 소멸자는 뮤텍스의 참조 카운트가 0이 될 때 삭제자로 지정된 unlock 함수를 호출해 준다.

+++

  • 관리하고 있는 자원을 정말로 복사한다.

Deep copy를 해야 하는 경우가 이것이다.

대표적인 경우가 string 클래스이다. string을 다른 string에 대입하면 힙에 똑같은 문자스트링이 하나 더 만들어 지게 된다. 그리고 각string 객체는 서로 다른 메모리 영역을 갖게된다.

  • 관리하고 있는 자원의 소유권을 넘긴다.

흔한 경우는 아니다. 특정한 자원에 대해 그 자원을 실제로 참조하는 RAII 객체를 오직 하나만 존재하게 하고싶을 때 사용한다. 대표적인 예가 auto_ptr 이다.

정리

  • RAII 객체의복사는 그 객체가 관리하는 자원의 복사 문제를 안고 가기 때문에, 그 자원을 어떻게 복사하느냐에 따라 RAII 객체의 복사 동작이 결정된다.
  • RAII 클래스에 구현하는 일반적인 복사 동작은 복사를 금지하거나 참조 카운팅을 해 주는 선으로 마무리하는 것이다. 하지만 이 외의 방법들도 가능하니 참고해 두자.

results matching ""

    No results matching ""