티스토리 뷰

728x90

자원관리 클래스의 복사 동작에 대해 진지하게 고찰하자.

객체 복사에서는 컴파일러가 제공하는 디폴트 함수(복사 생성자, 대입연산자등)가
자신이 원하는 동작을 못한다면 직접 설계하여야 한다.

힙 기반 자원 관리의 스마트 포인터는 힙에 생기지 않는 자원의 처리로는 부적합하다.
이 경우 자원 관리 클래스를 만들어야 할 필요성이 있다.

 

예제

Mutex 타입의 동기화 객체의 잠금과 해지 클래스 생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
void lock(Mutex *pm);         // pm이 가리키는 뮤텍스의 잠금
void unlock(Mutex *pm);     // pm이 가리키는 뮤텍스의 잠금해지
 
class Lock
{
public:
    explicit Lock(Mutex *pm) : mutexPtr(pm)
    {
        lock(mutexPtr);
    }
    ~Lock()
    {
        unlock(mutexPtr);
    }
private:
    Mutex* mutexPtr;
}
 
 
void main()
{
    // 차후 사용될 Mutex의 선언
    Mutex m;
}
 
 
void foo( Mutex& _Mutex)
{
    //생성자의 동작
    Lock m1(_Mutex);
 
    // TODO : 
 
    //소멸자의 동작
}
 
 
잠정적 문제 : Lock 객체가 복사된다면 어떻게 해야 될 것인가?  동기화 객체의 잠금이 해지될수도 있다.
 
Lock m11(&m);
Lock m12(m11);
void ()
{
    Lock m11(&m);
    {
        Lock m12(m11);
        ...
    }
    ...
}
cs

 

 

 

해법

1. 복사를 금지한다.(항목 6)
    private 맴버로 복사 생성자 및 대입 연산 통제
    Uncopyable 클래스를 상속(항목 6)
2. 관리하고 있는 자원에 대해 참조 카운팅을 한다.(세마포어의 동작형태)
   스마트 포인터 trl::shared_ptr가 참조 카운팅을 사용한다.
   trl::shared_ptr 은 일반적으로 카운트가 0이 될때 대상을 삭제하지만
   삭제자(소멸자)를 지정하여 카운트가 0이 될때의 동작을 함수호출이나 객체의 호출로
   변화가 시킨다.(trl::auto_ptr 은 바로 삭제된다.)

    ex) 스마트의 포인터를 Lock의 맴버로 사용하는 예

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::trl::shared_ptr<Mutex> mutexPtr;
}
cs

 3. 관리중인 자원을 깊은 복사한다.
 4. 관리하고 있는 자원의 소유권을 넘긴다.

결론

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

하지만 이 외의 방법들도 가능하니 참고하자.

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함
250x250