저번 시간에 만든 Custom smartpointer는 다음과 같은 단점이 있다.
1) 이미 만들어진 클래스 대상으로 사용불가.
2) 순환(Cycle) 문제.
2) 번 문제 예를 살펴보자.
using KnightRef = TSharedPtr<class Knight>;
class Knight : public RefCountable
{
public:
Knight()
{
cout << "Knight()" << endl;
}
~Knight()
{
cout << "~Knight()" << endl;
}
void SetTarget(KnightRef target)
{
_target = target;
}
KnightRef _target = nullptr;
};
int main()
{
// 1) 이미 만들어진 클래스 대상으로 사용 불가
// 2) 순환(Cycle) 문제
KnightRef k1(new Knight());
k1->ReleaseRef();
KnightRef k2(new Knight());
k2->ReleaseRef();
k1->SetTarget(k2);
k2->SetTarget(k1);
k1 = nullptr;
k2 = nullptr;
}
k1과 k2가 서로를 SharedPtr로 갖고 있다. k1을 따라가보면 생성될 때 RefCount가 증가하고 k2멤버변수에 복사될 때 RefCount가 증가한다. k1에 nullptr을 넣을 때 RefCount감소하는데 k2 멤버변수로 있던 k1은 k2가 소멸할 때 Release를 호출하지만, 이미 nullptr로 되어있어 RefCount가 감소하지 않는다. 이렇게 메모리 누수가 일어난다.
순환참조를 해결하는 방법으로 weak_ptr을 사용할 수 있다. 그전에 shared_ptr, weak_ptr에 대해 알아보자.
shared_ptr<Knight> spr(new Knight());
...
shared_ptr > _Ptr_base
...
private:
element_type* _Ptr{nullptr};
_Ref_count_base* _Rep{nullptr};
shared_ptr은 _Ptr_base를 상속받았는데 멤버변수로 _Ptr과 _Rep가 있다. _Ptr은 shared_ptr이 관리하는 포인터이고 _Rep는 Reference Count block이다.
shared_ptr은 두 가지 정보를 [T*][RefCountBlock] 각 주소에 갖고 있는 것이다.
shared_ptr<Knight> ptr = make_shared<Knight>()
template <class _Ty>
class _Ref_count_obj2 : public _Ref_count_base
그러나 make_shared를 사용하면 _Ref_count_obj2를 생성하는데 정보를 위와 다르게 [T* | RefCountBlock]으로 하나의 주소에 저장한다.
RefCountBlock은 두 개의 정보를 관리한다. 하나는 useCount 다른 하나는 weakCount이다.
shared_ptr관련 ref count가 useCount, weak_ptr관련 ref count가 weakCount이다. useCount가 0이면 관리 포인터를 해제하지만 weakCount는 0이 되어도 관리 메모리를 해제하지 않는다. 따라서 멤버변수로 shared_ptr를 가지고 있어 cycle이 생길때 weak_ptr을 사용하면 메모리 누수를 막을 수 있다.
weak_ptr을 사용할 때는 관리 메모리가 해제되었는지 확인하는 코드가 필요하다.
// RefCountingBlock은 어떤 정보를 가지고 있음?
// uses(shared), weak(weak)
weak_ptr<Knight> wpr = spr3;
// 관리 포인터가 살아있는지 확인하는 코드 필요
bool expired = wpr.expired();
// or
shared_ptr<Knight> spr4 = wpr.lock();
if (spr4 != nullptr)
{
}
1. 순환참조가 일어나는 과정은?
- 객체가 소멸하면서 멤버변수를 소멸 시키는 구조에서, 객체가 소멸되지 않아서 발생함.
2. 순환참조를 방지하는 방법은?
- weak_ptr 이 use_count를 높이지 않고, weak_count가 0보다 커도 관리 포인터를 해제할 수 있음을 활용.
- 순환참조가 일어날 수 있는 shared_ptr 멤버 변수에 weak_ptr을 사용한다.
3. weak_ptr 사용 단점은?
- weak_ptr 사용하면 다시 shared_ptr을 반환받는 코드가 필요함.
참조 : [C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버 - 인프런 | 강의 (inflearn.com)
Stomp allocator (0) | 2021.09.11 |
---|---|
Allocator (0) | 2021.09.11 |
Reference Counting (0) | 2021.09.08 |
Deadlock 탐지 (0) | 2021.09.06 |
Read-Writer lock (0) | 2021.09.03 |
댓글 영역