Fundamental of CS/: : C++

[Smart Pointer] Weak Pointer

Jay.P Morgan 2024. 7. 16. 11:08

 

 

weak_ptr 정리

1. weak_ptr이란?

weak_ptr은 shared_ptr과 함께 사용하는 스마트 포인터로, 객체를 참조하지만 레퍼런스 카운트에는 영향을 주지 않는 포인터입니다.

  • shared_ptr → 객체의 소유권을 가짐, 레퍼런스 카운트 증가
  • weak_ptr → 객체를 관찰만 함, 레퍼런스 카운트 증가 X

즉, weak_ptr은 객체의 생명주기를 유지하지 않습니다.

예:

객체
 ↑    ↑    ↑
SP1  SP2  WP1

이 경우:

  • shared_ptr 2개 → 레퍼런스 카운트 = 2
  • weak_ptr 1개 → 레퍼런스 카운트에 영향 없음

따라서 shared_ptr이 모두 사라지면 객체는 삭제되고, weak_ptr은 더 이상 유효하지 않은 상태가 된다.

 

 

2. weak_ptr이 필요한 이유

shared_ptr은 여러 객체가 하나의 객체를 공유할 수 있게 해준다.

하지만 문제는 순환 참조(Circular Reference)이다.

예:

Kitty shared_ptr → Nabi
Nabi shared_ptr → Kitty

서로가 서로를 shared_ptr로 가리키면:

Kitty
 ↑   ↓
Nabi

레퍼런스 카운트가 계속 1 이상으로 유지된다.

결과:

  • 외부 shared_ptr이 모두 제거됨
  • 하지만 서로가 서로를 참조하고 있음
  • 레퍼런스 카운트가 0이 되지 않음
  • 소멸자가 호출되지 않음
  • 메모리 릭 발생

이를 해결하기 위해 weak_ptr을 사용한다.

 

 

 

3. weak_ptr 동작 방식

weak_ptr은 shared_ptr을 참조하지만 소유권을 가지지 않는다.

예:

std::shared_ptr<Cat> cat = std::make_shared<Cat>();

std::weak_ptr<Cat> weakCat = cat;

상태:

Cat Object

shared_ptr
    ↓
 Cat Object
    ↑
weak_ptr

레퍼런스 카운트:

shared_ptr : 1
weak_ptr   : 영향 없음

 

 

4. weak_ptr 사용 방법

weak_ptr은 직접 객체에 접근할 수 없다.

반드시 lock()을 통해 shared_ptr로 변환해야 한다.

예:

auto sharedCat = weakCat.lock();

결과:

  • 객체가 존재하면 → 새로운 shared_ptr 반환
  • 객체가 삭제되었으면 → 빈 shared_ptr 반환

예:

if(sharedCat)
{
    sharedCat->Speak();
}
else
{
    std::cout << "객체가 존재하지 않음";
}

 

 

5. weak_ptr의 객체 생존 확인 방법

방법 1: lock()

가장 일반적인 방법.

auto ptr = weakPtr.lock();

if(ptr)
{
    ptr->Function();
}

객체가 살아있는 경우에만 사용 가능하다.

 

 

방법 2: expired()

객체가 삭제되었는지 확인한다.

if(weakPtr.expired())
{
    // 객체 삭제됨
}
  • true → 객체 없음
  • false → 객체 존재

 

 

6. shared_ptr 순환 참조 해결

기존 코드:

class Cat
{
    std::shared_ptr<Cat> friendCat;
};

문제:

Kitty
 ↓
Nabi
 ↓
Kitty

순환 참조 발생.

해결:

class Cat
{
    std::weak_ptr<Cat> friendCat;
};

변경 후:

Kitty
 ↓
weak_ptr
 ↓
Nabi

weak_ptr은 레퍼런스 카운트를 증가시키지 않기 때문에 순환 참조가 사라진다.

 

 

7. unique_ptr을 멤버 변수로 사용할 때 주의점

unique_ptr은 Exclusive Ownership을 보장한다.

즉:

하나의 객체
 ↓
하나의 unique_ptr

만 가능하다.

따라서 기본 복사가 불가능하다.

예:

class Cat
{
    std::unique_ptr<int> value;
};

이때:

Cat a;
Cat b = a;

는 컴파일 오류 발생.

이유:

복사를 하면:

Cat A
 unique_ptr
     ↓
   int 객체


Cat B
 unique_ptr
     ↓
   같은 int 객체

가 되어 unique_ptr의 소유권 규칙을 위반한다.

그래서 컴파일러는:

  • Copy Constructor 삭제
  • Copy Assignment 삭제

를 자동으로 수행한다.

 

 

 

8. unique_ptr 클래스에서 직접 복사 구현하기

정말 복사가 필요하다면 직접 Copy Constructor를 구현해야 한다.

방법:

  1. 새로운 메모리 생성
  2. 기존 값 복사
  3. 새로운 unique_ptr이 새 메모리를 소유

즉:

Cat A
 unique_ptr
     ↓
   int 10


Cat B
 unique_ptr
     ↓
   int 10

서로 다른 메모리를 가진 독립 객체가 된다.

 

 

9. shared_ptr을 멤버 변수로 사용할 때 주의점

shared_ptr은 복사가 가능하다.

따라서:

class Cat
{
    std::shared_ptr<int> value;
};

에서:

Cat a;
Cat b = a;

가능하다.

하지만 내부 동작은:

Cat A
 shared_ptr
      ↓
      int


Cat B
 shared_ptr
      ↓
      int

처럼 같은 객체를 공유한다.

즉 개발자가 기대한:

Cat A
 ↓
int A

Cat B
 ↓
int B

형태가 아니다.

 

 

 

10. shared_ptr 멤버 변수의 해결 방법

클래스 설계자가 의도한 경우라면 문제가 없다.

하지만 사용자 입장에서 혼동될 수 있기 때문에:

방법 1: 문서화

클래스에:

  • 내부적으로 shared ownership 사용
  • 복사 시 같은 객체 공유

라는 것을 명시한다.

 

 

 

방법 2: clone() 제공

깊은 복사가 필요하면:

Cat clone();

같은 함수를 제공한다.

동작:

원본 객체
 ↓
새로운 객체 생성
 ↓
내용 복사

즉 Deep Copy 인터페이스를 제공한다.

 

 

 

11. 실제 사례

대표적인 예:

OpenCV의 cv::Mat

특징:

  • 내부 데이터를 공유하는 방식
  • 복사 시 실제 데이터 복사가 아닌 참조 증가
  • 필요하면 clone()으로 Deep Copy 수행

 shared_ptr과 유사한 설계 방식을 사용한다.

 

 

 

핵심 정리

스마트 포인터 종류 특징 사용 목적
unique_ptr 하나의 소유자만 가능 독점 소유, 가장 기본적인 스마트 포인터
shared_ptr 여러 소유자 가능 객체 공유
weak_ptr 참조만 가능, 카운트 증가 X 순환 참조 방지

 

 

반드시 기억할 내용

  1. weak_ptr은 shared_ptr의 순환 참조 문제를 해결하기 위해 사용한다.
  2. weak_ptr은 직접 사용할 수 없고 lock()으로 shared_ptr을 얻어야 한다.
  3. unique_ptr은 복사가 불가능하며 필요하면 직접 Deep Copy를 구현해야 한다.
  4. shared_ptr 멤버 변수는 복사 시 같은 객체를 공유한다는 점을 반드시 인지해야 한다.
  5. 스마트 포인터의 동작 원리를 이해하면 메모리 관리와 클래스 설계를 안전하게 할 수 있다.

 

'Fundamental of CS > : : C++' 카테고리의 다른 글

[Templates] Introduction  (0) 2024.07.16
[Templates] C++ Templates  (0) 2024.07.16
[Smart Pointer] Shared Pointer  (0) 2024.07.16
[Smart Pointer] Unique Pointer (std::unique_ptr)  (0) 2024.07.16
[Smart Pointer] Smart Pointer  (0) 2024.07.16