티스토리 뷰

std::unique_ptr

특징

  • 리소스를 독점
  • 복제 불가 

 

메서드

이름 설명
get 리소스에 대한 포인터를 리턴
get_deleter delete 함수를 리턴
release 리소스에 대한 포인터를 리턴하고 그 리소스를 해제
reset 리소스를 리셋
swap 리소스를 맞바꿈

 

코드 예제

// 이것은 unique_ptr 예제
#include<iostream>
#include<memory>

using namespace std;
struct MyInt
{
	MyInt(int i) :i_(i) {}
	~MyInt()
	{
		cout << "Good bye from " << i_ << endl;
	}
	int i_;
};

int main()
{
	unique_ptr<MyInt> uniquePtr1{ new MyInt(1998) };
	cout << "uniquePtr1.get(): " << uniquePtr1.get() << endl;
	unique_ptr<MyInt> uniquePtr2{move(uniquePtr1)};
	cout << "uniquePtr1.get(): " << uniquePtr1.get() << endl;
	cout << "uniquePtr2.get(): " << uniquePtr2.get() << endl;
	// uniquePtr1에게 할당된 리소스가 uniquePtr2에게 옮겨졌다.
	// 그리고 uniquePtr1의 메모리는 0x00000000이 잡힌다. 
	{
		unique_ptr<MyInt> localPtr{ new MyInt(2003) };
	}
	//1998이 저장된 클래스는 해제되고 2011로 새롭게 생성된다. 
	uniquePtr2.reset(new MyInt(2011));
	MyInt* myInt = uniquePtr2.release();
	cout << "myInt location : " << myInt << endl;
	cout << "uniquePtr2.get(): " << uniquePtr2.get() << endl;
	//release로 myInt 포인터로 해당 리소스가 리턴됐고 uniquePtr2의 리소스는 해제됐다.
	delete myInt;

	unique_ptr<MyInt> uniquePtr3{ new MyInt(2017) };
	unique_ptr<MyInt> uniquePtr4{ new MyInt(2022) };
	cout << "uniquePtr3.get(): " << uniquePtr3.get() << endl;
	cout << "uniquePtr4.get(): " << uniquePtr4.get() << endl;

	swap(uniquePtr3,uniquePtr4);
	cout << "uniquePtr3.get(): " << uniquePtr3.get() << endl;
	cout << "uniquePtr4.get(): " << uniquePtr4.get() << endl;
}
// 이것은 unique_ptr의 배열 형태 예시
include <iomanip>
include<iostream>
include<memory>

using namespace std;
class MyStruct
{
public:
	MyStruct() :val(count)
	{
		cout << setw(15) << left << (void*)this << "Hello: " << val << endl;
		MyStruct::count++;
	}
	~MyStruct() {
		cout << setw(15) << left << (void*)this << "Good bye:" << val << endl;
		MyStruct::count--;
	};
private:
	int val;
	static int count;
};

int MyStruct::count = 0;

int main()
{
	// MyStruct 다섯 개로 구성된  myUniqueArray를 생성한다.
	{
		unique_ptr<MyStruct[]> myUniqueArray{ new MyStruct[5] };
	}
	//uniqueArray를 생성하고 이 배열의 한 원소에 새로운 MyStruct를 할당한다.
	{
		unique_ptr<MyStruct[]> myUniqueArray{ new MyStruct[1] };
		MyStruct myStruct;

		myUniqueArray[0] = myStruct;
	}
	//myUniqueArray를 생성하고 MyStruct에 새로운 myUniqueArray 원소를 할당
	{
		unique_ptr<MyStruct[]> myUniqueArray{ new MyStruct[1] };
		MyStruct myStruct;
		myStruct = myUniqueArray[0];

	}
}

 

std::shared_ptr

특징

  • 공유 변수에 대해 레퍼런스 카운터가 있다.
  • 레퍼런스 카운터는 자동으로 관리한다. (스코프에 벗어났다던가)
  • 레퍼런스 카운터가 0이 되면 리소스를 제거한다.

 

메서드

이름 설명
get 리소스에 대한 포인터를 리턴
get_deleter delete 함수를 리턴
reset 리소스를 리셋
swap 리소스를 맞바꿈
unique 현재 std::unique_ptr만 리소스를 소유하고 있는지 검사
use_count 레퍼런스 카운트 값을 리턴

 

코드 예제

// 이것은 shared_ptr 예제
#include <iostream>
#include <memory>

using namespace std;
class MyInt
{
public:
	MyInt(int v) :val(v)
	{
		cout << "Hello:" << val << endl;
	}
	~MyInt()
		{
			cout << "Good bye" << val << endl;
		}
	
private:
	int val;
};

int main()
{
	auto sharPtr = make_shared<MyInt>(1998);
	cout << "sharedPtr.usecount():" << sharPtr.use_count() << endl; //1
	{
		shared_ptr<MyInt>locSharPtr(sharPtr);
		cout << "locSharPtr.use_count(): " << locSharPtr.use_count() << endl; //2
		// 스코프를 벗어났으므로 레퍼런스 카운트가 1 감소
	}
	cout << "sharPtr,use_count(): " << sharPtr.use_count() << endl; //1

	shared_ptr<MyInt> glovSharPtr = sharPtr;
	cout << "sharPtr.use_count(): " << sharPtr.use_count() << endl; //2
	glovSharPtr.reset();
	cout << "sharPtr.use_count(): " << sharPtr.use_count() << endl; //1

	sharPtr = shared_ptr<MyInt>(new MyInt(2011)); // hello 2011
	// 기존 1998 클래스가 있는 메모리에 새로운 2011 클래스를 생성하더니
	// 1998 클래스는 해제되고 레퍼런스 카운트가 1 감소했다. 
	cout << "sharPtr.use_count(): " << sharPtr.use_count() << endl; //1
	//good bye 1998
	//good bye 2011
}

 

만약 자기 자신의 주소를 갖는 std::shared_ptr를 갖고 싶다면

//이것은 shared_ptr_from_this 예제
#include <iostream>
#include <memory>
using namespace std;
class ShareMe :public enable_shared_from_this<ShareMe>
{
public:
	shared_ptr<ShareMe> getShared()
	{
		return shared_from_this();
	}
};

int main()
{
	//동일한 ShareMe 오브젝트를 공유한다.
	shared_ptr<ShareMe> shareMe(new ShareMe);
	shared_ptr<ShareMe> shareMe1 = shareMe->getShared();

	//두 리소스에 대한 주소가 같다.
	cout << "Address of resource of shareMe " << (void*)shareMe.get() << " " << endl;
	cout << "Address of resource of shareMe1" << (void*)shareMe1.get() << "" << endl;

	cout << "shareMe.use_count():" << shareMe.use_count() << endl;
}

 

std::weak_ptr

특징

  • 레퍼런스 카운터를 수정하지 않는다.
  • std::shared_ptr의 순환 구조를 깨는데 도움이 된다.

 

메서드

이름 설명
expired 리소스가 삭제됐는지 검사
lock 리소스에 대한 std:: shared_ptr를 생성
reset 리소스를 리셋
swap 리소스를 맞바꿈
use_count 레퍼런스 카운터의 값을 리턴

코드 예제

// 이것은 weak_ptr 예제
#include <iostream>
#include <memory>

using namespace std;

class MyInt
{
public:
	MyInt(int i) :i_(i) {}
	int get() const { return i_; }
private:
	int i_;
};

int main()
{
	weak_ptr<MyInt> weakPtr;

	auto sharedPtr = make_shared<MyInt>(2011);
	cout << "sharedPtr.use_count(): " << sharedPtr.use_count() << endl; //1
	//weakPtr 초기화
	weakPtr = sharedPtr;
	cout << "weakPtr.use_count(): " << weakPtr.use_count() << endl; //1
	cout << "weakPtr.expired(): " << weakPtr.expired() << endl;

	weak_ptr<MyInt> weakPtr1(sharedPtr);

	//리소스를 참조한다.
	cout << "sharedPtr ->get()" << sharedPtr->get() << endl;
	//weakPtr는 사용하지 않는다.


	if (shared_ptr<MyInt> sharedPtr1 = weakPtr.lock())
	{
		cout << "sharedPtr->get(): " << sharedPtr -> get() << endl;
	}
	else
	{
		cout << "Don't get the resource!" << endl;
	}
	//weakPtr를 리셋한다.
	weakPtr.reset();
	if (shared_ptr<MyInt> sharedPtr1 = weakPtr.lock())
	{
		cout << "sharedPtr->get(): " << sharedPtr->get() << endl;// 이건 출력 안됨.
	}
	else
	{
		cout << "Don't get the resource!" << endl;
	}

	// weakPtr2와 weakPtr3를 맞바꾼다.
	shared_ptr<MyInt> sharedPtr2(new MyInt(2));
	shared_ptr<MyInt> sharedPtr3(new MyInt(3));
	weak_ptr<MyInt> weakPtr2(sharedPtr2);
	weak_ptr<MyInt> weakPtr3(sharedPtr3);

	if (shared_ptr<MyInt> sharedFromWeak2 = weakPtr2.lock())
	{
		cout << "sharedFromWeak->get(): " << sharedFromWeak2->get() << endl; //2

	}
	weakPtr2.swap(weakPtr3);
	if (shared_ptr<MyInt> sharedFromWeak2 = weakPtr2.lock())
	{
		cout << "sharedFromWeak2->get(): " << sharedFromWeak2->get() << endl; //3
	}

	swap(weakPtr2, weakPtr3);
	if (shared_ptr<MyInt> sharedFromWeak2 = weakPtr2.lock()) {
		cout << "sharedFromWeak2->get(): " << sharedFromWeak2->get() << endl; //2
	}

}

 

순환 참조가 발생하지 않도록 하는 방법 

shared_ptr를 사용하다보면 순환 참조가 발생할 수 있다. 쉽게 말하자면 shared_ptr의 리소스가 해제되지 않고 런타임상에 살아있다는 이야기가 되는데, 이런 현상이 발생되면 우리가 스마트 포인터를 사용하는 목적이 사라지게 된다. 

이때 이 문제를 해결하기 위해 사용되는 포인터가 weak_ptr이다. 

코드 예제

// 이것은 순환 참조에 대한 예제
#include <iostream>
#include <memory>
using namespace std;

struct Son;
struct Daughter;

struct Mother {
	~Mother()
	{
		cout << "Mother gone" << endl;
	}
	void setSon(const shared_ptr<Son> s)
	{
		mySon = s;
	}
	void setDaughter(const shared_ptr<Daughter> d)
	{
		myDaughter = d;
	}
	shared_ptr<const Son> mySon;
	weak_ptr<const Daughter> myDaughter;

};
	struct Son
	{
		Son(shared_ptr<Mother> m) :myMother(m) {};
		~Son()
			{
				cout << "Son gone" << endl;
			}
		shared_ptr<const Mother> myMother;
	};
	struct Daughter
	{
		Daughter(shared_ptr<Mother> m) :myMother(m) {};
		~Daughter()
		{
			cout << "Daughter gone" << endl;
		}
		shared_ptr<const Mother> myMother;
	};

	int main()
	{
		{
			//shared_ptr에 Mother 할당
			shared_ptr<Mother> mother = shared_ptr<Mother>(new Mother);
			cout << "count: " << mother.use_count() << endl;
			// 아들,딸 할당(각각 카운트는 1이다.) 
			shared_ptr<Son> son = shared_ptr<Son>(new Son(mother));
			cout << "Son created, count: " << son.use_count() << endl;
			shared_ptr<Daughter> daughter = shared_ptr<Daughter>(new Daughter(mother));
			cout << "Daughter created, count: " << son.use_count() << endl;
			// mother쪽에서 아들,딸 shared_ptr를 부름
			// shared_ptr로 불러진 Son은 카운트 =2
			// weak_ptr로 불러진 daughter은 카운트 =1
			mother->setSon(son);
			cout << "called Son, count: " << son.use_count() << endl;
			mother->setDaughter(daughter);
			cout << "called daughter, count: " << daughter.use_count() << endl;

			//결론: Son은 한번 참조되고 메모리 해제가 안됨 daughter만 됨.
		}
	}
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함