std::shared_ptr은 refrerence-counting smart pointer 중 하나.
std::shared_ptr은 포인터를 통해 객체의 공유 소유권을 유지하는 스마트 포인터이다.
스마트 포인터의 기본적인 특성인 자신이 소멸될 때 가리키고 있는 대상에 대해 자동으로 delete 해줘 메모리 누수 걱정은 없게 작성이 되어 있다.
여러 shared_ptr 객체가 동일한 객체를 소유 할 수 있고, 객체가 삭제되고 다음 중 하나가 발생하면 메모리가 해재된다.
-마지막으로 남아있는 shared_ptr을 소유하고 있는 객체가 삭제된다.
-마지막으로 남아있는 shared_ptr을 소유하고 있는 객체가 다른 포인터에 의해 operator= 또는 reset()으로 할당된 경우 객체가 파괴된다.
shared_ptr은 다른 객체에 대한 포인터를 저장하는 동안 객체의 소유권을 공유할 수 있으며 이 기능은 개체를 소유하는 동안 개체를 가리키는 데 사용할 수 있다. 저장된 포인터는 get(), 역참조와 비교 연산자에 의해 액세스 된 포인터이다.
사용된 카운터가 0이 되면 관리되는 포인터는 deleter에 전달된다.
멤버 함수
get : 저장된 포인터를 반환한다.
operator*, operator-> : 저장된 포인터를 역참조한다.
operator[] : 저장된 배열에 대해 인덱스로 접근할 수 있도록 한다.
use_count : 동일하게 관리되는 객체를 참조하는 shared_ptr의 수를 반환한다.
unique : 관리되는 객체가 오직 현재 shared_ptr 인스터스에서만 관리되는지 확인한다.
operator bool : 저장된 포인터가 null이 아닌지 체크한다.
owner_before : 공유 포인터의 소유자 기반 순서를 제공한다.
사용 예제
#include <iostream>
#include <memory>
class TestClass
{
public:
void TestFunc()
{
std::cout << "hahaha" << std::endl;
}
public:
TestClass()
{
std::cout << "생성자 호출" << std::endl;
}
~TestClass()
{
std::cout << "소멸자 호출" << std::endl;
}
};
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
new int;
std::shared_ptr<TestClass> TempClass(new TestClass());
TempClass->TestFunc();
}
결과
생성자 호출
hahaha
소멸자 호출
생성자 호출 -> hahaha -> 소멸자 호출이 출력되고
동적할당을 new int, new TestClass() 두가지를 했지만
new TestClass()는 delete삭제가 되고 new int는 삭제가 되지 않아 릭이 남았다.
std::shared_ptr의 래퍼런스 카운트가 0이 되면서 TestClass()를 삭제했기 때문이다.
동적 메모리관리를 이렇게 편하게 할 수 가 있는데. 문제점은 무엇이 있을까?
문제점
shared_ptr은 래퍼런스 카운트가 0 이되면 가리키는 객체를 메모리에서 해제 시킨다고 했는데.
서로를 순환참조하는 shared_ptr이 있다면 어떻게 될까요
객체 A와 객체 B가 있다고 가정을하고 이객체들은 shared_ptr을 하나 씩 가지고 있을때
A는 B의, B는 A의 shared_ptr을 가리키고 있다면
A가 삭제가 되기 위해서는 A를 가리키고있는 shared_ptr의 래퍼런스 카운트가 0이 되어야하는데
그럼 B가 파괴가 되어야합니다. 근데 B가 파괴가 되려면 A가 파괴가 되어야하고 A가 파괴되려면 B가 파괴되어야하고.....
이러지도 저러지도 못하는 상황이 됩니다.
#include <iostream>
#include <memory>
class TestClass
{
public:
std::shared_ptr<TestClass> m_Other_Class;
void TestFunc()
{
std::cout << "hahaha" << std::endl;
}
void Set_Class(std::shared_ptr<TestClass> _Value)
{
m_Other_Class = _Value;
}
TestClass()
{
std::cout << "생성자 호출" << std::endl;
}
~TestClass()
{
std::cout << "소멸자 호출" << std::endl;
}
};
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
std::shared_ptr<TestClass> T_A = std::make_shared<TestClass>();
std::shared_ptr<TestClass> T_B = std::make_shared<TestClass>();
T_A->Set_Class(T_B);
T_B->Set_Class(T_A);
}
문제 코드 결과
생성자 호출
생성자 호출
소멸자는 호출 되지 않고 릭도 남았습니다.
해결방안
shared_ptr자체에 내재되어 있는 문제이기 때문에 shared_ptr로는 해결을 할 수 없습니다.
순환 참조 문제를 해결하기 위해서는 std::weak_ptr을 사용하면 됩니다.
std::weak_ptr에 대한 정리는 다음글에서 마무리하겠습니다.
참고
https://hojak99.tistory.com/270
'게임 프로그래밍 > C++ 기초' 카테고리의 다른 글
C++ 얕은복사(Shallow Copy) 깊은복사(Deep Copy) (2) | 2020.03.04 |
---|---|
std::weak_ptr (0) | 2020.03.03 |
C++ new, malloc (0) | 2020.03.02 |
오버로딩 (Overloading) (0) | 2020.03.02 |
오버라이딩 (Overriding) (0) | 2020.03.02 |