Post List

2014년 12월 25일 목요일

[STL] shared_ptr, weak_ptr

shared_ptr

가장 많이 사용되는 C++ Standard Library의 Smart Pointer 이다.

 * 기능
 - 참조 Counter를 이용하여 Pointer의 복사가 가능하다. (auto_ptr의 경우에는 복사시 원래 Pointer가 NULL 처리됨)
 - delete 연산 필요없이 자동으로 메모리에서 해제된다. (이건 Smart Pointer니깐 당연한 것)

기능으로 따지면 위의 2가지 정도라고 볼 수 있지만, 정말 편리하게 사용 될 수 있다.
과거 불가능한것 까진 아니었지만, 구현이 복잡한 경우에 대해서 편리하게 적용이 가능하다.

 1. MFC Class 같이 복사할 수 없는 객체를 복사할 경우 유용 (Class내부 변수 or Collection 활용)
    (예제는 링크 참조 http://icysword.blog.me/140211570931)

 2. Multi-Thread 환경에서 안정성 확보 (Reference Count만 해당, 객체는 보장 안됨)

1. namespace와 Header File
[-] Collapse
#include <memory>
using namespace std::tr1;



2. 선언, 대입연산 및 Reference Count

 - 복사, 대입이 가능하다
 - Reference Count 증가 : 복사, 대입
 - Reference Count 감소 : Pointer가 소멸
 - shared_ptr Delete : Reference Count가 0이 되면 소멸
 - reset()을 이용하여 다른 객체를 참조하도록 설정이 가능하다. (그냥 대입해도 되므로 별로 쓸일은 없다.)

[-] Collapse
#include <memory>

class CObj {...};

void Func(std::tr1::shared_ptr<CObj> p_obj) { ... }

int main()
{
    std::tr1::shared_ptr<CObj> obj1(new CObj());    // Reference Count : 1
    std::tr1::shared_ptr<CObj> obj2(new CObj());    // Reference Count : 1

    // 복사 가능
    obj2(obj1);                                                       // 기존 obj2 delete, obj1의 Reference Count : 2
    // 대입 가능
    std::tr1::shared_ptr<CObj> obj3 = obj2;            // Reference Count : 3

    Func(obj3);                                                      // 호출시 Reference Count : 4
                                                                           // 실행후  Reference Count : 3

    // reset : 다른 객체를 참조하도록 설정, 인자가 없으면 참조 포기
    obj3.reset();                                                      // Reference Count : 2
    return 0;
}





3. 배열 사용

 - shared_ptr의 소멸은 객체애 대하여 delete 연산을 한다. (delete[]는 해주지 않는다.)
 - 즉 new [] 는 지원되나 delete [] 가 지원되지 않아서 Memory Leak이 발생한다.
 - deleter를 지정하여 해결이 가능하다.

[-] Collapse
#include <memory>

class CObj {...};

template<typename T>
struct ArrayDeleter { void operator() (T* p) { delete [] p; } };

void Func(std::tr1::shared_ptr<CObj> p_obj) { ... }

// 생성자의 두번째 인자로 deleter class를 넘기면 해결됨
std::tr1::shared_ptr<int> arrInt( new int[1024], ArrayDeleter<int>() );

4. Casting 및 참조

 - static_pointer_cast(), const_pointer_cast(), dynamic_pointer_cast() 로 Casting 가능
 - 참조
    * shared_ptr::get() : 주소 반환
    * shared_ptr::operator * : 객체 자체를 반환 (일반 Pointer 와 같음)
    * shared_ptr::operator-> : get()->와 같음 (일반 Pointer와 같음)
[-] Collapse
#include <memory>

class CParent {...};
class CChild : public CParent {...};

std::tr1::shared_ptr<CParent> P1( new CChild());

std::tr1::shared_ptr<CChild> C1 = static_pointer_cast<CChild>(P1); // Casting 하여 대입

// 참조방법 4가지
CParent* P2 = P1.get();
P1.get()->Func();
*(P1).Func();
P1->Func();





5. Circular References

 A, B 객체가 각각 서로에 대하여 shared_ptr을 가지고 있을 경우 Reference Counter가 0이 되지 않아서 메모리 해제가 안 될 수도 있다.
 개발을 하다보면 서로 Pointer를 가지고 있는 경우가 많이 발생하는데 이럴 경우에는 한쪽에 weak_ptr을 적용해야 한다.
[-] Collapse
#include <memory>
#include <vector>

class CMember;
typedef std::tr1::shared_ptr<CMember> CMemPtr;
class CDepartment;
typedef std::tr1::shared_ptr<CDepartment> CDeptPtr;

class CMember
{
public:
    void Set(const CDeptPtr& p_Dept) { m_Dept = p_Dept; )
protected:
    CDeptPtr m_Dept;
};

class CDepartment
{
public:
    void Add(const CMemPtr& p_Mem) { m_List.push_back(p_Mem); }
protected:
    std::vector<CMemPtr> m_List;
};

int main()
{
    CDeptPtr D1(new CDepartment);

    for (unsigned int i = 0; i < 10; i++)
    {
        CMemPtr M(new CMember);

        // Circular Refences 발생
        D->Add(M);
        M->Set(D);
    }

    D.reset(); // Refence Counter = 11인 상태에서 10으로 바뀌므로 메모리가 해제되지 않음
    return 0;
}






weak_ptr

 - shared_ptr을 참조만 할 뿐이지 Reference Counter를 증가시키지 않는다.
 - weak_ptr, shared_ptr로 부터 대입, 복사 가능 (일반 Pointer로부터의 대입은 불가)
 - shared_ptr로 convert 가능 (일반 Pointer로는 불가)

 위의 예제에서 weak_ptr을 사용하여 상호참조가 일어나지 않도록 해보겠다.
[-] Collapse
#include <memory>
#include <vector>

class CMember;
typedef std::tr1::shared_ptr<CMember> CMemPtr;
class CDepartment;
typedef std::tr1::shared_ptr<CDepartment> CDeptPtr;
typedef std::tr1::weak_ptr<CDepartment> CDeptWeakPtr;

class CMember
{
public:
    void Set(const CDeptPtr& p_Dept) { m_Dept = p_Dept; )
protected:
    CDeptWeakPtr m_Dept;
};

class CDepartment
{
public:
    void Add(const CMemPtr& p_Mem) { m_List.push_back(p_Mem); }
protected:
    std::vector<CMemPtr> m_List;
};

int main()
{
    CDeptPtr D1(new CDepartment);

    for (unsigned int i = 0; i < 10; i++)
    {
        CMemPtr M(new CMember);

        // Circular Refences 발생
        D->Add(M);
        M->Set(D); // 더이상 Reference Counter를 증가시키지 않는다.
    }

    D.reset(); // Refence Counter = 1인 상태에서 0으로 바뀌므로 메모리가 해제됨
    return 0;
}







참조 : http://sweeper.egloos.com/2826435

댓글 없음:

댓글 쓰기