3.스마트 포인터

Edit

ProudNet은 스마트 포인터 클래스를 보유하고 있습니다.

스마트 포인터란, 생성된 객체을 참조하는 변수들이 존재하는 한 해당 객체의 존재 자체를 보장하는 역할을 합니다. 그리고 해당 객체을 참조하는 변수들이 더 이상 존재하지 않을 때만 그 객체는 파괴됩니다. 스마트 포인터는 개발자가 만든 버그로 인해 이미 파괴된 객체를 참조하는 문제(dangling)나 미처 객체를 파괴하지 않는 문제(leak)를 해소하는 역할을 합니다.

스마트 포인터 변수는 복사될 때마다 객체 참조 카운트가 1씩 증가합니다. 아래 그림에서 Object Instance는 참조되는 모든 스마트 포인터 변수가 더 이상 없을 때까지, 즉 참조 카운트가 0이 될때까지 존재하게 됩니다.

그림 3-1스마트 포인터와 객체간의 관계

ProudNet의 스마트 포인터는 Proud.RefCount입니다.

아래는 스마트 포인터를 사용하는 예제 코드입니다.

class A {...};

void Foo()
{
    // A 객체가 생성
    Proud::RefCount<A> a(new A);
    
    // 변수 b가 변수 a와 공유됨. A의 참조 카운트는 2가 됨.
    Proud::RefCount<A> b = a;
    
    // 변수 a가 해제됨. 그러나 A는 참조 카운트가 아직 1이므로 파괴되지 않음.
    a = Proud::RefCount<A>();
    
    // 변수 b가 해제됨. 더 이상 A를 참조하는 변수가 없으므로 A가 파괴됨. (즉 delete가 호출됨)
    b = Proud::RefCount<A>();
}

만약 객체를 참조하는 스마트 포인터가 여러 군데 있는 상태에서 강제로 객체를 파괴하고 싶을 때가 있습니다. 예를 들어, 열려있는 파일 핸들을 보유한 객체를 스마트 포인터에서 참조하고 있을 경우, 당장 그 파일 핸들을 보유한 객체를 파괴해야 하는 때가 있을 것입니다. 하지만 스마트 포인터가 여기 저기서 그 객체를 참조하고 있을 경우 명시적으로 객체가 파괴되는 시점을 알 수 없기 때문에 난관에 봉착할 수 있습니다.

이러한 경우를 위해 3.1 Dispose Pattern을 구사할 수 있습니다. 3.1 Dispose Pattern을 통해 이미 여러 군데에서 참조하고 있는 객체의 파괴를 명시적으로 수행할 수 있습니다.

3.1Dispose Pattern

Dispose Pattern은 한개 이상의 스마트 포인터가 참조하고 있는 객체가 파괴될 시점을 정확히 모름에도 불구하고 객체를 명시적으로 파괴하는 효과를 얻기 위한 프로그램 패턴입니다.

스마트 클래스로 다뤄질 객체에 Dispose Pattern을 구사하려면 객체의 멤버 변수로서 '자기 상태'를 가지게 합니다. 자기 상태라 함은 이미 객체가 파괴되어서 더 이상 사용될 수 없는 상태인지를 의미해야 합니다. 만약 자기 상태가 '이미 파괴된 상태'인 경우 객체 참조시 에러를 발생시키고, 그렇지 않은 경우 정상 수행을 하도록 만들어야 합니다.

아래는 Dispose Pattern를 구사한 예입니다.

class A
{
    // true인 경우 이 객체는 파괴(dispose)된 상태임을 의미한다.
    bool m_disposed;
    public:
    
    A()
    {
        // 갓 생성된 객체는 dispose되어있지 않은 상태이다.
        m_disposed = false;
    }

    ~A()
    {
        Dispose();
    }

    void Dispose()
    {
        if(m_disposed == false)
        {
            // 객체 파괴에 관련된 실제 수행을 한다.
            // 예를 들어 갖고 있던 파일 핸들을 닫는다.
            ...
        
            m_disposed = true;
        }
    }
};

typedef Proud::RefCount<A> APtr;

void Foo()
{
    APtr a(new A); // 객체 생성
    APtr b = a;     // 두 개의 스마트 포인터 변수가 한 객체를 공유
    
    a->Dispose();   // 강제로 객체를 파괴합니다.
    
    // 이제 a,b 모두 객체는 파괴된 상태입니다.
    // 따라서 a,b가 참조하고 있는 객체를 접근하면 안됩니다.
    ...
}

참고로, Dispose Pattern은 Java나 C# 등 스마트 포인터와 Garbage Collector가 있는 프로그래밍 언어에서도 다뤄지고 있습니다.