가장 많이 사용되는 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;
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;
}
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>() );
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();
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;
}
#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;
}
#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
댓글 없음:
댓글 쓰기