std::shared_ptr와 std::weak_ptr: 공유 소유권과 순환 참조
8강. std::shared_ptr와 std::weak_ptr: 공유 소유권과 순환 참조
이번 강의에서 해결할 문제
여러 객체가 같은 대상의 수명을 함께 책임져야 할 때 shared_ptr를 사용할 수 있습니다. 하지만 편하다는 이유로 기본 선택하면 실제 소유자가 불명확해지고 서로를 공유 소유하는 순환 참조 때문에 객체가 파괴되지 않을 수 있습니다.
학습 목표
먼저 알아야 할 핵심 개념
shared_ptr 복사본들은 하나의 제어 블록을 공유하고 강한 참조 수가 0이 될 때 대상을 파괴합니다. weak_ptr는 강한 참조 수를 늘리지 않는 관찰자입니다. 대상 사용 시 lock()으로 일시적인 shared_ptr를 얻고 실패하면 만료된 것입니다.
왜 이 문제가 위험한가
두 shared_ptr 소유 객체가 서로를 강하게 참조하면 외부 소유자가 없어져도 참조 수가 0이 되지 않습니다. 또한 같은 raw 주소로 독립 shared_ptr 두 개를 만들면 제어 블록이 둘이라 이중 해제 위험이 생깁니다. “스마트”라는 이름이 잘못된 소유권 모델을 자동으로 고치지는 않습니다.
가장 작은 예제로 시작하기
#include <iostream>
#include <memory>
#include <string>
#include <utility>
struct Worker {
explicit Worker(std::string value) : name{std::move(value)} {}
~Worker() { std::cout << "destroy " << name << '\n'; }
std::string name;
std::weak_ptr<Worker> partner;
};
void printPartner(const std::weak_ptr<Worker>& observer) {
if (auto partner = observer.lock()) {
std::cout << partner->name << '\n';
} else {
std::cout << "expired\n";
}
}
int main() {
auto first = std::make_shared<Worker>("first");
auto second = std::make_shared<Worker>("second");
first->partner = second;
printPartner(first->partner);
second.reset();
printPartner(first->partner);
}
코드 한 줄씩 이해하기
make_shared는 대상과 제어 블록을 올바르게 연결합니다.partner는 비소유 관계라weak_ptr입니다.- 첫
lock()은 성공하고 접근 중 수명을 보장하는 지역shared_ptr를 만듭니다. second.reset()뒤 강한 소유자가 없으므로second대상이 파괴됩니다.- 다음
lock()은 빈 결과를 반환해expired를 출력합니다.
실행 결과와 메모리·동작 흐름
second, destroy second, expired, 마지막에 destroy first가 출력됩니다. 약한 관찰은 대상 수명을 늘리지 않으므로 외부 소유자가 사라질 때 정상적으로 파괴됩니다. 사용 시점에 성공한 lock() 결과만 역참조합니다.
단계별 실습
흔한 실수와 해결 방법
언제 사용하고 언제 피할까
독립 구성 요소가 대상 수명을 실제로 함께 책임지는 경우에만 shared_ptr를 사용합니다. 트리의 부모, 캐시 관찰자, 콜백 대상처럼 수명을 늘리면 안 되는 관계는 weak_ptr나 명시적 비소유 계약을 고려합니다.
게임 개발 연결
Unreal의 TSharedPtr도 일반 비-UObject C++ 객체의 공동 소유에 사용하지만 UObject GC를 대체하지 않습니다. UObject를 std::shared_ptr 또는 TSharedPtr로 소유하거나 직접 삭제하면 안 됩니다. UObject 참조는 UPROPERTY, TObjectPtr, TWeakObjectPtr 등 엔진 버전과 용도에 맞는 GC 추적 규칙을 따릅니다.
이해 점검 질문 3개
- shared_ptr 순환 참조에서 외부 변수가 사라져도 객체가 남는 이유는 무엇인가요?
- weak_ptr의
lock()결과를 지역 변수로 보관해야 하는 이유는 무엇인가요? - UObject 수명을 shared_ptr로 관리하면 안 되는 이유는 무엇인가요?
핵심 요약
다음 강의 연결
9강. 복사 생성자와 복사 대입에서 소유 자원을 가진 객체를 복사할 때 일어나는 일을 분석합니다.
std::shared_ptr와 std::weak_ptr: 공유 소유권과 순환 참조 미니 퀴즈
선택 즉시 정답과 해설을 확인할 수 있습니다. 결과는 이 브라우저에만 저장됩니다.
학습을 마쳤나요?
직접 실습과 점검 질문까지 확인한 뒤 완료로 표시하세요.