배움 저장소
[C++] unique_ptr, shared_ptr, weak_ptr 본문
1. unique_ptr
여러 포인터가 하나의 대상을 가르키는 걸 방지해준다. 해당 값을 가리키는 ptr를 한 개로 제한하고 싶을때 사용하면 된다. 만약 여러 unique_ptr이 하나의 대상을 가르키면 컴파일 에러가 난다. 특정 데이터의 소유권을 다른 unique_ptr로 옮기려면 move()함수를 이용하자
unique_ptr<A> ptr1 (new A);
// Error: can't copy unique_ptr
unique_ptr<A> ptr2 = ptr1;
// Works, resource now stored in ptr2
unique_ptr<A> ptr2 = move(ptr1);
예제
// C++ program to illustrate the use of unique_ptr
#include <iostream>
#include <memory>
using namespace std;
class A {
public:
void show(){ cout << "A::show()" << endl; }
};
int main()
{
unique_ptr<A> p1(new A);
p1->show(); // -> "A::show()"
cout << p1.get() << endl; // -> 0x1c4ac20
// transfers ownership to p2
unique_ptr<A> p2 = move(p1);
p2->show(); // ->"A::show()"
cout << p1.get() << endl; // -> 0
cout << p2.get() << endl; // -> 0x1c4ac20
// transfers ownership to p3
unique_ptr<A> p3 = move(p2);
p3->show(); // -> "A::show()"
cout << p1.get() << endl; // -> 0
cout << p2.get() << endl; // -> 0
cout << p3.get() << endl; // -> 0x1c4ac20
return 0;
}
만약 unique_ptr을 반환하는 함수가 있다면 사용법은 다음과 같다.
unique_ptr<A> fun()
{
unique_ptr<A> ptr(new A);
/* ...
... */
return ptr;
}
만약 함수가 호출되면서 해당 unique_ptr이 저장되지 않는다면 unique_ptr 정보는 지워질 것이다. delete를 따로 사용해주어야하는 포인터와 달리 Memory 관리를 하기가 편하다.
2. shared_ptr
unique_ptr과 달리 여러 shared_ptr이 동일한 대상을 가리킬 수 있다. 그리고 해당 대상을 가리키고 있는 shared_ptr이 몇 개인지 get()함수를 사용하여 확인할 수 있다. 동일한 대상을 가르키는 여러 shared_ptr를 사용하고 싶을 때 사용하자. Constructor가 실행될 때 count가 늘어나고 Destructor가 실행될 때 count가 줄어든다. count=0이되면 메모리 해제된다.
#include <iostream>
#include <memory>
using namespace std;
class A {
public:
void show()
{
cout << "A::show()" << endl;
}
};
int main()
{
A* test = new A(); // 해당 ptr는 counting 고려대상이 아니다
shared_ptr<A> p1(test);
// shared_ptr<A> p1(new A);
cout << p1.get() << endl; // -> 02800510
p1->show(); // -> "A::show()"
shared_ptr<A> p2(p1);
p2->show(); // -> "A::show()"
cout << p1.get() << endl; // -> 02800510
cout << p2.get() << endl; // -> 02800510
// Returns the number of shared_ptr objects
// referring to the same managed object.
cout << p1.use_count() << endl; // -> 2
cout << p2.use_count() << endl; // -> 2
// Relinquishes ownership of p1 on the object
// and pointer becomes NULL
p1.reset();
cout << p1.get() << endl; // -> 00000000
cout << p2.use_count() << endl; // -> 1
cout << p2.get() << endl; // -> 02800510
}
3. weak_ptr
shared_ptr과 동일한 기능을 제공한다. 한 대상을 여러 weak_ptr이 가르킬 수 있다. shared_ptr과 다른점은 Destructor를 호출할 때 사용되는 counting에 아무 영향을 주지 않는다. 이 덕분에 순환참조 문제를 해결할 수 있다.
순환참조의 예제는 수까락님 블로그에 잘 소개되어 있다. http://sweeper.egloos.com/2826435
해당 글의 9) Circular references이다.
#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);
}
private:
typedef vector<UserPtr> MemberList;
MemberList m_MemberList;
};
typedef shared_ptr<Party> PartyPtr;
class User
{
public:
void SetParty(const PartyPtr& party)
{
m_Party = party;
}
private:
PartyPtr m_Party;
};
int _tmain(int argc, _TCHAR* argv[])
{
PartyPtr party(new Party);
for (int i = 0; i < 5; i++)
{
// 이 user는 이 스코프 안에서 소멸되지만,
// 아래 party->AddMember로 인해 이 스코프가 종료되어도 user의 refCount = 1
UserPtr user(new User);
// 아래 과정에서 순환 참조가 발생한다.
party->AddMember(user);
user->SetParty(party);
}
// 여기에서 party.reset을 수행해 보지만,
// 5명의 파티원이 party 객체를 물고 있어 아직 refCount = 5 의 상태
// 따라서, party가 소멸되지 못하고, party의 vector에 저장된 user 객체들 역시 소멸되지 못한다.
party.reset();
return 0;
}
위 상황에서 파티 class 내부에 shared_ptr 대신 weak_ptr을 사용하면 해결!
주의
weak_ptr은 shared_ptr이나 unique_ptr처럼 (new 연산자)를 활용하여 자신을 초기화할 수 없다. weak_ptr은 shared_ptr 혹은 같은 weak_ptr을 사용하여 초기화해야 한다.
다음 예제는 수까락님 블로그에서 가져왔다
#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;
}
참고
https://www.geeksforgeeks.org/auto_ptr-unique_ptr-shared_ptr-weak_ptr-2/
http://sweeper.egloos.com/2826435
'Programming Language > C++' 카테고리의 다른 글
[홍정모의 따라하며 배우는 C++] 0. 시작해봅시다. (0) | 2021.12.10 |
---|---|
[C++] Template (0) | 2021.11.13 |
[C/C++] Left Shift and Right Shift (0) | 2021.11.11 |
부호가 있는 이진수 1000은 0일까? (0) | 2021.10.19 |
[C++] Iterator (0) | 2021.10.09 |