프로그래밍/C++

스마트 포인터

우대비 2022. 11. 10. 19:37
반응형

스마트 포인터

- 그냥 포인터 기능이 있는 클래스 탬플릿 정도로 생각하면 됨

 

종류 

- shared_ptr

- weak_ptr

- unique_ptr

 

shared_ptr 왜 써?

class Knight
{
public:
	Knight() { cout << "Knight 생성" << endl; }
	~Knight() { cout << "Knight 삭제" << endl; }
public:
	int _hp = 100;
	Knight* _target;
};


Knight* k1 = new Knight();
knight* k2 = new Knight();
k1->target = k2;
delete k2;

cout << k1->target->_hp << endl;

위 코드에서 k2는 delete되지만

k1의 target으로 들어간 k2의 주소값이 사라지는건 아니기 때문에

k1에서 해당 주소값의 엉뚱한 값을 건드릴 수 있게 됨

 

하나의 주소값을 여러 객체에서 사용할 때

"해당 주소값을 사용하는 객체"들을 "통합적으로 관리"할 무언가가 필요하다는 것!!

 

그것이 바로

shared_ptr

shared - 공유해버려따~

shared_ptr은 무엇을 공유해버렸을까?

 

바로 shared_ptr안에 있는 refCount라는 객체를 공유했음!!

이 refCount는 객체 포인터가 복사 될 때마다 ( 복사 대입 연산자든 복사 생성자든 )

count를 ++ !!!

 

그리고 복사된 객체 포인터의 소멸자가 호출 될 때마다

count를 -- !!!

 

근데 --를 했는데 count가 0이 된다!? 그럼 그때 delete를 해버리는거

 

class Knight
{
public:
	Knight() { cout << "Knight 생성" << endl; }
	~Knight() { cout << "Knight 삭제" << endl; }
public:
	int _hp = 100;
	shared_ptr<Knight> _target; // shared_ptr!!
};


shared_ptr<Knight> k1 = make_shared<Knight>();
{
    shared_ptr<Knight> k2 = make_shared<Knight>();	
}

k2는 {   } 안에서만 유효하기 때문에 당연하게도

영역 밖으로 나가면 삭제됨!

 

shared_ptr<Knight> k1 = make_shared<Knight>();
{
    shared_ptr<Knight> k2 = make_shared<Knight>();	
    k1->_target = k2;
}

하지만 k1의 target으로 복사가 되면

count가 ++ 되면서 삭제가 안됨..!

 

즉 shared_ptr로 만든 객체는 언제 삭제된다?

복사된 다른 모든 객체의 소멸자가 호출 되어서 count가 0이될때

 

그렇다면 이렇게 해버리면 어떻게 될까?

shared_ptr<Knight> k1 = make_shared<Knight>();
{
    shared_ptr<Knight> k2 = make_shared<Knight>();	
    k1->_target = k2;
    k2->_target = k1;
}

k2를 참조하는 k1을 참조하는 k2를 참조하는 k1을 참조하는 k2를 참조하는 k1.....

서로 꽈배기처럼 꼬이는 루프에 빠져 삭제 안됨..

그럴때는 weak_ptr을 쓰면 됨

 

weak_ptr

class Knight
{
public:
	Knight() { cout << "Knight 생성" << endl; }
	~Knight() {cout << "Knight 삭제" << endl; }
	void Attack()
	{
		if (_target.expired() == false) // _target이 유효한지 체크
		{
			shared_ptr<Knight> sptr = _target.lock(); // _target 사용
			cout << "HP : " << sptr->_hp << endl;
		}
	}
public:
	int _hp = 100;
	weak_ptr<Knight> _target; // weak_ptr !!!
};

shared_ptr<Knight> k1 = make_shared<Knight>();
{
    shared_ptr<Knight> k2 = make_shared<Knight>();	
    k1->_target = k2;
    k2->_target = k1;
}

*week_ptr"끼리는 count를 따로 세지는 않아서 루프에 빠지지는 않음!

 

count를 안세면 그냥 포인터 쓰는것 처럼

유효하지 않는 주소값에 대한 접근을 할 수도 있는게 아닌가? 생각할 수도 있는데

 

weak_ptr는 다른 shared_ptr소유자에 대한 "약한 참조를 제공"하는데

객체의 생명주기가 다 되어 NULL이 되었는지 체크할 수 있는 기능 2개가 있음

 

1. 객체가 만료되지는 않았는지 조심스럽게 체크

if (_target.expired() == false) // _target이 유효한지 체크

2. lock()을 통해 nullptr인지 체크

shared_ptr<Knight> sptr = _target.lock();

즉 weak_ptr은 "복사받은 객체가 살아있는지 확인"하는 역할을 하는거

 

어떻게!?

 

weak_ptr는 직접적으로 자원을 할당받을 수 없고

shared_ptr로 부터 복사를 받아야하는데 

*복사 받으면 weak_ptr 안에 있는 두개의 count가 변함 ( Uses, Weaks )

 

1. Uses : 1  => week_ptr과 연결된 shared_ptr 갯수

2. Weaks : 1 => shared_ptr로부터 복사받은 week_ptr의 갯수

 

shared_ptr<Knight> sptr = _target.lock();

여기서 lock()를 사용해 shared_ptr을 만들면

Uses : 2 가 됨

 

객체의 생명주기가 다 되어 NULL이 될때 Uses --

 

그리고 Uses가 0이 되면

if (_target.expired() == false) // 막힘!
shared_ptr<Knight> sptr = _target.lock(); // 막힘!!

위의 코드에서 걸러져서

유효하지 않은 주소값에 대한 접근을 막음..!

 

unique_ptr

위의 스마트 포인터들과는 별개로 생각하는게 좋음

그냥 단순히 복사가 안되는 포인터임

std::unique_ptr<Knight> uptr = std::make_unique<Knight>();
std::unique_ptr<Knight> upp = uptr; // 오류
std::unique_ptr<Knight> upp = std::move(uptr);

하지만 오른값 참조를 통해서 이동은 가능!

반응형
LIST

'프로그래밍 > C++' 카테고리의 다른 글

C++ 파일 탐색  (0) 2023.10.17
LNK2019, LNK1120 해결법  (0) 2022.12.15
C++ Lambda  (0) 2022.11.10
전달 참조(보편 참조)와 forward  (0) 2022.11.09
오른값(rvalue)과 이동 대입 연산자  (0) 2022.11.09