스마트 포인터
- 그냥 포인터 기능이 있는 클래스 탬플릿 정도로 생각하면 됨
종류
- 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);
하지만 오른값 참조를 통해서 이동은 가능!
'프로그래밍 > 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 |