본문으로 건너뛰기
모던 C++ 심화LESSON 08

std::shared_ptr와 std::weak_ptr: 공유 소유권과 순환 참조

난이도중급 → 심화
예상 시간70분
선수지식이전 강의

8강. std::shared_ptr와 std::weak_ptr: 공유 소유권과 순환 참조

이번 강의에서 해결할 문제​

여러 객체가 같은 대상의 수명을 함께 책임져야 할 때 shared_ptr를 사용할 수 있습니다. 하지만 편하다는 이유로 기본 선택하면 실제 소유자가 불명확해지고 서로를 공유 소유하는 순환 참조 때문에 객체가 파괴되지 않을 수 있습니다.

학습 목표​

먼저 알아야 할 핵심 개념​

shared_ptr 복사본들은 하나의 제어 블록을 공유하고 강한 참조 수가 0이 될 때 대상을 파괴합니다. weak_ptr는 강한 참조 수를 늘리지 않는 관찰자입니다. 대상 사용 시 lock()으로 일시적인 shared_ptr를 얻고 실패하면 만료된 것입니다.

왜 이 문제가 위험한가​

두 shared_ptr 소유 객체가 서로를 강하게 참조하면 외부 소유자가 없어져도 참조 수가 0이 되지 않습니다. 또한 같은 raw 주소로 독립 shared_ptr 두 개를 만들면 제어 블록이 둘이라 이중 해제 위험이 생깁니다. “스마트”라는 이름이 잘못된 소유권 모델을 자동으로 고치지는 않습니다.

가장 작은 예제로 시작하기​

weak_observer.cpp
#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);
}

코드 한 줄씩 이해하기​

  1. make_shared는 대상과 제어 블록을 올바르게 연결합니다.
  2. partner는 비소유 관계라 weak_ptr입니다.
  3. 첫 lock()은 성공하고 접근 중 수명을 보장하는 지역 shared_ptr를 만듭니다.
  4. second.reset() 뒤 강한 소유자가 없으므로 second 대상이 파괴됩니다.
  5. 다음 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개​

  1. shared_ptr 순환 참조에서 외부 변수가 사라져도 객체가 남는 이유는 무엇인가요?
  2. weak_ptr의 lock() 결과를 지역 변수로 보관해야 하는 이유는 무엇인가요?
  3. UObject 수명을 shared_ptr로 관리하면 안 되는 이유는 무엇인가요?

핵심 요약​

다음 강의 연결​

9강. 복사 생성자와 복사 대입에서 소유 자원을 가진 객체를 복사할 때 일어나는 일을 분석합니다.

MINI QUIZ

std::shared_ptr와 std::weak_ptr: 공유 소유권과 순환 참조 미니 퀴즈

선택 즉시 정답과 해설을 확인할 수 있습니다. 결과는 이 브라우저에만 저장됩니다.

0 / 2
  1. 문제 1“std::shared_ptr와 std::weak_ptr: 공유 소유권과 순환 참조”에서 다음 단계로 넘어가기 전에 확인할 핵심은 무엇인가요?
  2. 문제 2“std::shared_ptr와 std::weak_ptr: 공유 소유권과 순환 참조”의 작업 기준으로 ‘같은 raw 주소로 shared_ptr 두 개 생성’을 진단하거나 바로잡은 선택은 무엇인가요?
LESSON STATUS

학습을 마쳤나요?

직접 실습과 점검 질문까지 확인한 뒤 완료로 표시하세요.

8강. std::shared_ptr와 std::weak_ptr: 공유 소유권과 순환 참조 미완료 상태