2016년 3월 20일 일요일

[Effective C++] 항목 14 : 자원관리 클래스의 복사 동작에 대해 진지하게 고찰하자.

- 모든 자원이 힙에서 생기지는 않기 때문에, 우리는 필요에 따라, 자원 관리 클래스를 직접 만들어야 할 경우도 있습니다.

- RAII 기법에 따라, 뮤텍스 잠금을 관리하는 클래스를 만든다면, 아래 예제 처럼 작성 할 수 있습니다.

RAII 패턴은 C++ 같이 개발자가 직접 resource 관리를 해주어야 하는 언어에서 leak 을 방지하기 위한 중요한 기법으로 해당 리소스의 사용 scope이 끝날 경우에 자동으로 해제를 해주며 exception이 발생하거나 하는 경우에도 획득한 자원이 해제됨을 보장하여
robust code 코드를 작성할 수 있다.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Lock
{
public:
    explicit Lock(Mutex *pm) : mutexPtr(pm)
    {
        lock(mutexPtr);
    }
    ~Lock() { unlock(mutexPtr); }
private:
    Mutex *mutexPtr;
}
void main()
{
    Mutex m;
    {
        Lock m1(&m);
    }
}

 - 생성시, 잠금을 걸고, 블럭을 나가면서 잠금을 해제할 것 입니다.
  만약, 같은 객체로 Lock을 한번 더 하게 된다면, 어떻게 되어야 할까요?
?
1
2
Lock m1(&m);
Lock m2(m1);


아래의 규칙을 참고하여, 해당 하는 내용을 작성해 봅니다.

1. 복사를 금지 합니다.
   복사하면 안되는 RAII 클래스에 대해서는 반드시 복사가 되지 않도록 막아야 합니다.
?
1
2
class Lock : private Uncopyable
{
  
2. 관리하고 있는 자원에 대해 참조 카운팅을 수행합니다.
   해당 자원을 참조하는 객체의 개수에 대한 카운트를 증가시키는 식으로 RAII 객체의 복사동작을 만들어야 합니다.
   tr1::shared_ptr를 사용 할 수 있는데, 기본 동작이 참조 카운트가 0이 되면 대상을 삭제해 버리기 때문에,
   삭제자를 지정하여 사용 합니다. 삭제자는 참조 카운트가 0이 되었을 때 호출되는 함수 혹은 함수 객체를 말합니다.
?
1
2
3
4
5
6
7
8
9
10
11
class Lock
{
public:
    explicit Lock(Mutex *pm) : mutexPtr(pm, unlock)
    {
        lock( mutexPtr.get() );
    }
private:
    std::tr1::shared_ptr<mutex> mutexPtr;
}
</mutex>
- 위 코드를 보면, 소멸자 선언이 사라졌습니다.
  클래스의 소멸자는 비정적 데이터 멤버의 소멸자를 자동으로 호출하게 되어 있습니다.
  이 '비정적 데이터 멤버'에 해당하는 것이 mutexPtr입니다.
  그런데, mutexPtr의 소멸자는 뮤텍스의 참조 카운트가 0이 될 때, 삭제자를 자동으로 호출합니다.

  위 코드에서는 컴파일러가 생성한 소멸자를 통해, 뮤텍스의 참조 카운트가 0이 되면, unlock을 호출합니다.

3. 관리하고 있는 자원을 진짜로 복사합니다.
 - 때에 따라서는 자원을 원하는 대로 복사 할 수도 있습니다.
   자원 관리 객체를 복사하면 그 객체가 둘러싸고 있는 자원까지 복사되어야 합니다. [깊은 복사(deep copy)]

4. 관리하고 있는 자원의 소유권을 옮깁니다.
 - 특정한 자원에 대해 그 자원을 실제로 참조하는 RAII 객체는 딱 하나만 존재하도록 만들고 싶을때,
   auto_ptr과 같이 소유권을 이동하는 동작을 생각 할 수 있습니다.

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

댓글 없음:

댓글 쓰기

[Effective C++] 항목 30 : 인라인 함수는 미주알고주알 따져서 이해해 두자.

인라인 함수를 사용하면 컴파일러가 함수 본문에 대해 문맥별 최적화를 걸기가 용이해집니다. 인라인 함수의 아이디어는  함수 호출문을 그 함수의 본문으로 바꿔치기하자는 것  남발했다가는 코드의 크기가 커질 게 뻔하다. 인라인 함수로 부풀려진 ...