Skip to content
DarkKaiser의 블로그
DarkKaiser의 블로그
  • 개발 관련 자료(노션)
  • Raspberry Pi(노션)
  • WD My Cloud(노션)
  • GitHub
DarkKaiser의 블로그

[boost] weak_ptr

DarkKaiser, 2015년 3월 20일2023년 9월 6일

출처 : http://sweeper.egloos.com/3059940

1. shared_ptr

shared_ptr의 내용은 다음 링크를 참고하기 바라며, 특히 3-9 Circular reference 챕터를 자세히 읽어보기 바란다.(위 링크엔 shared_ptr의 circular reference에 대한 예제가 포함되어 있다)

2. weak_ptr

shared_ptr은 자신이 참조하고 있는 객체(메모리 주소)에 대해 reference counting을 함으로써, 객체의 수명에 직접적으로 관여한다. shared_ptr 객체 하나가 소멸되더라도, 동일한 메모리 주소를 참조하고 있는 다른 shared_ptr 객체가 있으면 참조하고 있던 메모리 주소의 객체는 소멸되지 않는다.

하지만, weak_ptr은 shared_ptr을 관리하기 위한 reference count에 포함되지 않는다. 즉, shared_ptr의 객체만 참조할 뿐, shared_ptr의 reference count를 올리지 않는 것이다.

사실 weak_ptr이 shared_ptr을 참조할 때 shared_ptr의 weak reference count는 증가시킨다. 객체의 생명 주기에 관여하는 strong reference count를 올리지 않는 것 뿐이다. (shared_ptr, weak_ptr 객체를 디버거로 살펴보면 strong/weak refCount가 따로 표시된다)(weak reference count는 객체의 소멸에는 전혀 관여하지 않으니 헤깔리지 말도록!)

위에서 얘기한 것처럼, weak_ptr은 shared_ptr의 참조자라고 표현하는 것이 맞을 듯 하다. 같은 weak_ptr 또는 shared_ptr로부터만 복사 생성/대입 연산이 가능하며, shared_ptr로만 convert가 가능하다.

따라서, weak_ptr<_Ty>는 _Ty 포인터에 대해 직접 access가 불가능하며,(shared_ptr의 get() 메쏘드 같은 녀석이 아예 없다) _Ty 포인터에 엑세스를 원하면 lock 메써드를 통해 shared_ptr로 convert 한 뒤, shared_ptr의 get 메쏘드를 사용해야 한다.

shared_ptr<_Ty> lock() const
{      
    // convert to shared_ptr
    return (shared_ptr<_Elem>(*this, false));
}

그리고  expired 함수를 통해 자신이 참조하고 있는 shared_ptr의 상태(즉, weak_ptr의 상태)를 체크할 수 있다.

bool expired() const
{      
    // return true if resource no longer exists
    return (this->_Expired());
}

3. 예제

지금까지의 내용에 대한 이해를 돕기 위해 wikipedia에서 소개하는 예제부터 살펴보자.

#include <memory>    //for shared_ptr/weak_ptr
#include <iostream>
 
using namespace std;
 
int main(int argc, char** argv)
{
    // strong refCount = 1
    shared_ptr<int> sp1(new int(5));
 
    // shared_ptr sp1으로부터 복사 생성
    // weak_ptr이 참조하여, strong refCount = 1, weak refCount = 1
    weak_ptr<int> wp1 = sp1;
    {
        // wp1이 참조하고 있던 sp1을 weak_ptr::lock 메써드를 이용해 sp2가 참조
        // string refCount = 2, weak refCount = 1
        shared_ptr<int> sp2 = wp1.lock();
        if (sp2)
        {
            // weak_ptr<_Ty>의 _Ty 포인터에 엑세스 하려면
            // 이렇게 shared_ptr로 convert하는 방법 밖에 없다
        }
        // sp2가 여기에서 소멸, strong RefCount = 1, weak refCount = 1
    }
 
    // sp1.reset으로 인해 strong refCount = 0, 즉 sp1 소멸
    // wp1이 참조하고 있던 sp1이 소멸되었으므로, wp1은 expired
    sp1.reset();
 
    // expired된 wp1은 참조하고 있는 shared_ptr이 없다.
    // 따라서, sp3도 empty
    shared_ptr<int> sp3 = wp1.lock();
    if (sp3)
    {
        // 여기 문장은 실행되지 않는다
    }
 
    return 0;
}

4. Circular reference 회피 예제

shared_ptr 문서의 circular reference 예제를 weak_ptr을 사용해 개선시켜 보았다.

아래 예제와 비교해 보길 바란다.

#include <memory>    // for shared_ptr
#include <vector>
 
using namespace std;
 
class User;
typedef shared_ptr<User> UserPtr;
 
class Party
{
public:
    Party() {}
    ~Party() { m_MemberList.clear(); }
 
public:
    void AddMember(const UserPtr& member)
    {
        m_MemberList.push_back(member);
    }
 
    void RemoveMember()
    {
        // 제거 코드
    }
 
private:
    typedef vector<UserPtr> MemberList;
    MemberList m_MemberList;
};
typedef shared_ptr<Party> PartyPtr;
typedef weak_ptr<Party> PartyWeakPtr;
 
class User
{
public:
    void SetParty(const PartyPtr& party)
    {
        m_Party = party;
    }
 
    void LeaveParty()
    {
        if (m_Party)
        {
            // shared_ptr로 convert 한 뒤, 파티에서 제거
            // 만약, Party 클래스의 RemoveMember가 이 User에 대해 먼저 수행되었으면,
            // m_Party는 expired 상태
            PartyPtr partyPtr = m_Party.lock();
            if (partyPtr)
            {
                partyPtr->RemoveMember();
            }
        } 
    }
 
private:
    // PartyPtr m_Party;
    PartyWeakPtr m_Party;    // weak_ptr을 사용함으로써, 상호 참조 회피
};
 
 
int _tmain(int argc, _TCHAR* argv[])
{
    // strong refCount = 1;
    PartyPtr party(new Party);
 
    for (int i = 0; i < 5; i++)
    {
        // 이 UserPtr user는 이 스코프 안에서 소멸되지만,
        // 아래 party->AddMember로 인해 이 스코프가 종료되어도 user의 refCount = 1
        UserPtr user(new User);
         
        party->AddMember(user);
     
        // weak_ptr로 참조하기에 party의 strong refCount = 1
        user->SetParty(party);
    }
    // for 루프 이후 strong refCount = 1, weak refCount = 5
 
    // 여기에서 party.reset을 수행하면, strong refCount = 0
    // 즉, 파티가 소멸되고 그 과정에서 m_MemberList가 clear -> user들의 strong RefCount = 0 -> user 소멸
    // party와 5개의 user 모두 정상적으로 소멸
    party.reset();
 
    return 0;
}

5. weak_ptr 정리

weak_ptr은 다음과 같은 경우에 사용하면 유용하다.

  • 어떠한 객체를 참조하되, 객체의 수명에 영향을 주고 싶지 않은 경우
  • 그리고 매번 특정 객체의 ID로 컬렉션에서 검색하고 싶지 않을 경우
  • 그러면서 dangling pointer의 잠재 위험성을 없애고 싶을 때
C/C++/VC++ boostweak_ptr

글 내비게이션

Previous post
Next post

답글 남기기 응답 취소

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

최신 글

  • AssertJ 소개testCompile ‘org.assertj:assertj-core:3.6.2’ 2017년 9월 14일
  • 자주 사용되는 Lombok 어노테이션 2017년 9월 14일
  • 유니코드 #3 2017년 9월 14일
  • 유니코드 #2 2017년 9월 14일
  • 유니코드 #1 2017년 9월 14일

최신 댓글

    카테고리

    • 개인 자료 (1)
      • 일기 (1)
    • 주절주절 (7)
    • 프로그래밍 갤러리 (16)
    • 프로그래밍 언어 (186)
      • Java (29)
      • C/C++/VC++ (114)
      • C# (11)
      • Visual Basic (6)
      • 안드로이드 (9)
      • Objective-C (5)
      • JavaScript (4)
      • JSP/Servlet (2)
      • Python (4)
      • 어셈블러 (1)
    • 개발++ (44)
      • Book (11)
        • Joel On Software (10)
      • 프로젝트 관리 (6)
      • Maven (1)
      • 디버깅 (1)
      • DirectX (1)
      • Silverlight (1)
      • RESTful (1)
      • Hacking (1)
      • WDM (4)
      • VoIP (5)
      • 기타 (1)
    • 개발 도구 (15)
      • eclipse (14)
      • Sublime Text (1)
    • 네트워크 (7)
    • 설치 및 배포 (7)
      • InstallShield (2)
      • NSIS (4)
    • 버전 관리 (9)
      • Git (2)
      • CVS (2)
      • Subversion (5)
    • 데이터베이스 (7)
      • Oracle (3)
      • Sybase (2)
      • MS-SQL (2)
    • 단위테스트 (3)
      • JUnit (1)
      • NUnit (2)
    • 버그추적시스템 (2)
      • mantis (2)
    • 운영체제 (7)
      • Windows (5)
      • 리눅스 (2)
    • WAS (3)
      • WebLogic (3)
    • 디자인패턴 (1)
    • 디지털 이미지 프로세싱 (16)

    태그

    ArrayList ATL BMP CAB CAB 파일 CD-ROM COM Downcasting for each GetLastError() Internet Explorer Java JDT JoelOnSoftware LinkedList Lokbok netsh NUnit Python SAX StringBuilder Subverion SVN TR1 unicows Upcasting VisualStudio WAVE weak_ptr WebClient Wrap 내장 객체 레이아웃 마우스 문자 스트림 바이트 스트림 배포 비스타 빌드 서브클래싱 스트림 오피스파일구별 유니코드 지역클래스 타입 라이브러리

    메타

    • 로그인
    • 엔트리 피드
    • 댓글 피드
    • WordPress.org
    ©2025 DarkKaiser의 블로그 | WordPress Theme by SuperbThemes