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


자원 관리 클래스의 주축 아이디어

자원 획득 즉 초기화 (RAII) --> 전 시간에 포스팅


1. auto_ptr

2.shared_ptr

힙에 생기지 않는 자원은 위의

두 스마트포인터로 처리해 주지 못한다.




예제) 동적 할당되지 않는 경우의 자원 관리 클래스

스레드 동기화를 위한 Mutex 타입의 객체가 있다.

위의 객체가 제공하는 함수 중엔 lock와 unlock가 있다.


void lock(Mutex *pm) --> pm이 가리키는 뮤텍스를 잠금

void unlick(Mutex *pm)  --> 뮤텍스 잠금 해제


이제 뮤텍스의 잠금을 관리하는 클래스를 만들어 보자.

이전에 걸어 놓았던 뮤텍스의 잠금을 잊지 말고 풀어주기 위함.

이런 용도의 클래스는 RAII 법칙을 따라

생성 시에 자원을 획득하고, 소멸 시에 그 자원을 해제 한다.


class Lock{

private:

Metex* mutexPtr;

public:

explicit Lock(Mutex *pm) : mutexPtr(pm) {   // 생성과 동시에 자원 획득

lock (mutexPtr);                                     // 이 클래스에서는 잠금을 걸어 준다.

}

~Lock { unlock(mutexPtr) }                           // 자원 해제

}                                                                            // 이 클래스에서는 잠금을 해제.


int main(){

Mutex m;                             // 뮤텍스 정의

...

{

Lock m1(&m);              // 뮤텍스에 잠금을 건다.

...

}                                         // 블럭이 끝나면 Lock의 소멸자가 호출되면서

    // 뮤텍스의 잠금을 풀어주게 된다.




문제제기

Lock 객체가 복사된다면 어떻게 해야 할까?

위와 같은 스레드 동기화 객체에 대해서는

사본에 대한 의미가 없다. 위와 같은 경우가

아니라도 RAII 클래스를 만들 때 복사에 대해

주의를 기울여야 한다.


해결책

1. 복사를 금지 한다.

위와 같은 예제의 경우 사본에 대한

의미가 없기 때문에, 복사를 금지 한다.

복사를 금지하는 방법에 대해서는

지난 포스팅에서 언급했기 때문에 패스~


2. 관리하고 있는 자원에 대해 참조 카운팅을 수행한다.

shard_ptr (RCSP)이 참조 카운팅 방식의 스마트 포인터 라는 것을

지난 포스팅을 통해 알고 있다. 그리고 그 방식 또한 알고 있다.

해당 자원을 참조하는 객체의 개수를 카운팅하는

방식으로 복사를 진행하겠다 이 말이다. 


때문에 자원관리 객체 내에서 shared_ptr을 써보겠다.

Lock 클래스 내에서 Mutex* mutexPtr 이 아니라

shared_ptr<Mutex> mutexPtr; 로 바꿔주자는 말이다.




하지만 이 자원 관리 객체를 만든 의미를 다시 한번 생각해보자.

뮤텍스 객체를 잠그고 해제하는 용도이지 뮤텍스 객체를 사용하고

제거해버리는 용도가 아니다. 하지만 shared_ptr은 뮤텍스 객체를

가리키고 있고, 스마트 포인터 특성에 따라 객체가 없어지면서

뮤텍스 객체를 삭제해 버린다. 해결할 수 있는 방법이 없을까?


다행히도 shared_ptr은 삭제자 지정을 허용한다.

무슨말인고 하면 shared_ptr이 유지하는 참조 카운트가

0이 되면 무조건 소멸이 아니라 특정 함수 혹은 함수 객체를

호출할 수 있다는 것이다. 생성자의 두 번째 매개변수로 넣어 준다.




class Lock{

private:

shared_ptr<Mutex> mutexPtr;

public:

explicit Lock(Mutex* pm) : mutexPtr (pm, unlock) {

lock(mutexPtr.get()); --> 다음 포스팅에서 다룰 것이다.

}

};


이렇게 만들어주면 shared_ptr은 자신이 소멸 시에

가리키는 객체를 삭제해주는 것이 아니라 unlock() 함수를 호출한다.

또한 위의 클래스에는 소멸자가 없는데, 기본 소멸자를 호출한다.




3. 관리하고 있는 자원을 진짜 복사한다.

"자원을 다 썻을 때 각각의 사본을 확실히 해제한다" 는

전제 하에 이 정책을 들이도록 하자.

또한 자원관리 객체는 깊은 복사를 수행해야 한다.


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

어디서 들어본 말인고 하니 auto_ptr의 복사 동작이다.

즉 그 자원을 실제로 참조하는 RAII 객체는 단 하나만

유지하겠다는 것이다. (흔한 경우는 아님).




POINT!!

1. RAII 객체의 복사는 그 객체가 관리하는 자원의 복사 문제를 안고가기 때문에,

 그 자원을 어떻게 복사하느냐에 따라 RAII 객체의 복사 동작이 결정된다.


2. RAII 클래스에 구현하는 일반적인 복사 동작은 복사를 금지하는 것,

그리고 참조 카운팅을 해주는 방법이다.



+ Recent posts