std::weak_ptr
개요
std::weak_ptr
는 std::shared_ptr
가 관리하는 객체에 대한 비소유(non-owning) 참조를 제공하는 스마트 포인터입니다. std::shared_ptr
와 달리 std::weak_ptr
는 참조 카운트를 증가시키지 않으므로 순환 참조(circular reference) 문제를 해결하는 데 사용됩니다.
C++ 버전별 주요 키워드 도입 시기
- C++11:
std::weak_ptr
가 도입되었습니다.
내용 설명
std::weak_ptr
는 std::shared_ptr
인스턴스를 가리키지만, 해당 객체의 생명 주기에 영향을 주지 않습니다. 즉, std::weak_ptr
가 가리키는 객체는 마지막 std::shared_ptr
가 소멸될 때 함께 소멸되며, std::weak_ptr
는 더 이상 유효하지 않은 객체를 가리키게 됩니다.
주요 멤버 함수는 다음과 같습니다.
expired()
: 가리키는 객체가 이미 소멸되었는지 확인합니다.lock()
:std::weak_ptr
가 가리키는 객체를 참조하는 새로운std::shared_ptr
를 생성합니다. 만약 객체가 이미 소멸되었다면, 비어있는std::shared_ptr
를 반환합니다.use_count()
: 객체를 참조하는std::shared_ptr
의 개수를 반환합니다. (디버깅 용도로 주로 사용)
std::weak_ptr
는 std::shared_ptr
만으로 해결할 수 없는 순환 참조 문제를 방지하기 위해 필수적입니다. 두 객체가 서로를 std::shared_ptr
로 가리키면 참조 카운트가 0이 되지 않아 메모리 누수가 발생하지만, 한쪽 또는 양쪽을 std::weak_ptr
로 바꾸면 이 문제를 해결할 수 있습니다.
예제 코드
#include <iostream>
#include <memory>
#include <string>
class ScopeTest
{
public:
ScopeTest(int val) : m_val(val)
{
std::cout << "Constructor: " << m_val << '\n';
}
~ScopeTest()
{
std::cout << "Destructor:" << m_val << '\n';
}
void test()
{
std::cout << "Val: " << m_val << '\n';
}
std::weak_ptr<ScopeTest> m_partner;
int m_val;
};
void f1()
{
auto t = std::make_shared<ScopeTest>(1);
t->test();
std::cout << "Count: " << t.use_count() << '\n';
{
auto t2 = t;
t2->test();
std::cout << "Count: " << t.use_count() << '\n';
}
std::cout << "Count: " << t.use_count() << '\n';
}
void f2()
{
auto t4 = std::make_shared<ScopeTest>(11);
std::cout << "Count t4: " << t4.use_count() << '\n';
auto t5 = std::make_shared<ScopeTest>(12);
std::cout << "Count t5: " << t5.use_count() << '\n';
t4->m_partner = t5;
std::cout << "Count t5: " << t5.use_count() << '\n';
t5->m_partner = t4;
std::cout << "Count t4: " << t4.use_count() << '\n';
if (!t4->m_partner.expired())
{
auto t4_partner_shard = t4->m_partner.lock();
std::cout << t4_partner_shard->m_val << std::endl;
std::cout << "Count t5: " << t5.use_count() << '\n';
}
}
int main()
{
f1();
std::cout << '\n';
f2();
return 0;
}
실행 결과
Constructor: 1
Val: 1
Count: 1
Val: 1
Count: 2
Count: 1
Destructor:1
Constructor: 11
Count t4: 1
Constructor: 12
Count t5: 1
Count t5: 1
Count t4: 1
12
Count t5: 2
Destructor:12
Destructor:11
활용팁
- 순환 참조 방지:
std::weak_ptr
의 가장 중요한 용도입니다. 클래스들이 서로를std::shared_ptr
로 참조해야 할 때, 한쪽을std::weak_ptr
로 만들어 순환 참조를 끊고 메모리 누수를 방지할 수 있습니다. - 캐시(Cache) 구현: 객체에 대한 참조가 필요하지만, 해당 객체의 수명을 연장하고 싶지 않을 때 유용합니다. 캐시된 객체가 다른 곳에서 더 이상 사용되지 않아 소멸되면, 캐시는
expired()
를 통해 이를 감지하고 무효한 참조를 제거할 수 있습니다. - 안전한 접근:
lock()
함수를 통해std::shared_ptr
를 얻은 후 객체에 접근하면, 다중 스레드 환경에서도 객체가 접근 도중에 소멸되는 것을 방지하여 안전하게 사용할 수 있습니다.
'개발 > C++ (98,03,11,14,17,20,23)' 카테고리의 다른 글
Modern C++ : std::shared_ptr (11, 17) (0) | 2025.10.14 |
---|---|
Modern C++ : std::unique_ptr (11, 14) (0) | 2025.10.13 |
Modern C++ : std::format (20) (0) | 2025.10.12 |
Modern C++ : std::ranges (20) (0) | 2025.10.11 |
Modern C++ : std::Attributes (11, 14, 17, 20) (0) | 2025.10.10 |
Modern C++ : std::any (17) (0) | 2025.09.30 |
Modern C++ : std::variant (17) (0) | 2025.09.29 |
Modern C++ : std::optional (17) (0) | 2025.09.28 |