순환문제란 shared_ptr을 서로 교차해서 참조하는 경우에 그 포인터를 해제시켜도 해제되지 않고 메모리에 계속 남아있는 현상을 말한다. 아래 상황을 보자. (참고로 코드를 직접 실행해 보지는 않았음 그냥 상황만 보셈)
class Knight
{
sharerd_ptr<Knight> _target;
}
void main()
{
sharerd_ptr<Knight> k1 = make_shared<Knight>();
sharerd_ptr<Knight> k2 = make_shared<Knight>();
k1._target = k2;
k2._target = k1;
k1 = nullptr;
k2 = nullptr;
}
위처럼 두 Knight 객체의 스마트 포인터가 서로를 참조하고 있을 때, k1과 k2가 서로의 Reference Count를 증가시키고 있다.
이 때 우리 생각대로라면 k1 = nullptr이 되었을 때 k1 자체의 Reference Count도 감소하고, k2의 Reference Count도 k1의 소멸자가 호출되었을 때 멤버 변수인 _target의 소멸자도 같이 호출되면서 감소하게 되어야 한다. 그러나 k2가 k1을 참조하고 있어 Reference Count가 0이 되지 않아 소멸자가 호출되지 못하면서 k2의 Reference Count가 1 남게된다. 마찬가지로 k1도 1 남아서 결국 둘 다 소멸하지 못하게 된다.
이렇게 서로 참조하는 상황 뿐만 아니라 한 클래스가 다른 클래스를 소유하는 상황에서도 많이 발생한다. 예를 들어보자.
class Knight
{
shared_ptr<Knight> _target;
shared_ptr<Inventory> _inven;
}
class Inventory
{
Inventory(Knight){}
shared_ptr<Knight> _owner;
}
void main()
{
sharerd_ptr<Knight> k1 = make_shared<Knight>();
k1->_inven = new Inventory(k1);
}
위처럼 소유되는 상황에서도 상호참조가 발생한다.
해결 방법
1. 일단 사용자가 상호 참조를 조심하면서 사용하는 방법이 있다. 위 같은 상황이 발생하지 않도록 조심하자.
예를 들어 두 번째 상황에서 shared_ptr<Knight> _owner; 문장을 Knight& _owner; 로 변경하면 해결된다. Inventory 클래스는 Knight 클래스 객체의 Reference Count를 증가시키지 않게 되므로.
2. weak_ptr로 해결 가능하다. weak_ptr 사용에 대해서는 다음 글에서 스마트 포인터들을 공부하며 같이 알아보자.
'C++' 카테고리의 다른 글
Casting (Static, Dynamic, Reinterpret ) (0) | 2023.03.28 |
---|---|
스마트 포인터 (0) | 2022.03.29 |
STL 컨테이너별 간략 특징 정리 (0) | 2022.02.21 |
TLS(Thread Local Storage) (0) | 2022.02.01 |
메모리 모델 (0) | 2022.02.01 |