현대적인 C++에서는 직접적인 포인터를 사용하지 않고 스마트 포인터를 이용하여 간접적으로 활용하는게 일반적이다.
언리얼 엔진만 보더라도 전부 스마트 포인터로 되어있다.
스마트 포인터 : 포인터를 알맞는 정책에 따라 관리하는 객체 (포인터를 래핑해서 사용)
shared_ptr
레퍼런스 카운트를 통해 레퍼런스 카운트가 0일때만 해제된다.
명시적으로 delete를 해줄 필요 없이 특정 조건(레퍼런스 카운트 == 0)을 만족하면 알아서 delete를 해준다.
make_shared<>()를 통해 초기화 시켜주는게 성능면에서 조금 이점이 있다고 한다. (메모리 블럭을 한번에 만들어준다는 차이가 있음)
간단하게 구현한 코드
template<typename T>
class SharedPtr
{
public:
SharedPtr() {}
SharedPtr(T* ptr) : _ptr(ptr)
{
if (_ptr != nullptr)
{
_block = new RefCountBlock();
cout << "RefCount : " << _block->_refCount << endl;
}
}
SharedPtr(const SharedPtr& sptr) : _ptr(sptr._ptr), _block(sptr._block)
{
if (_ptr != nullptr)
{
_block->_refCount++;
cout << "RefCount : " << _block->_refCount << endl;
}
}
void operator=(const SharedPtr& sptr)
{
_ptr = sptr._ptr;
_block = sptr._block;
if (_ptr != nullptr)
{
_block->_refCount++;
cout << "RefCount : " << _block->_refCount << endl;
}
}
~SharedPtr()
{
if (_ptr != nullptr)
{
_block->_refCount--;
cout << "RefCount : " << _block->_refCount << endl;
}
if (_block->_refCount == 0)
{
delete _ptr;
delete _block;
cout << "Delete Data" << endl;
}
}
public:
T* _ptr = nullptr;
RefCountBlock* _block = nullptr;
};
사이클이 발생하는 경우 소멸이 되지 않는 문제가 있다.
사이클이 발생하지 않는지 잘 확인해서 사이클을 끊어주어야 한다.
weak_ptr
ref count(객체를 참고하는게 몇개인지)와 더불어 weak count(weak_ptr이 몇개가 참조 중인지)가 같이 관리된다.
shared_ptr과 다르게 객체 삭제 시점에 ref count block은 삭제하지 않고 냅둔다.
또한 shared_ptr처럼 바로 사용할수는 없고 expired()로 삭제 여부를 검사 후 lock()을 통해 shared_ptr를 반환 받아 사용해야 한다.
weak_ptr<Knight> _target;
if (_target) // 오류! shared_ptr처럼 사용 불가능
{
// ..
}
// ------------
weak_ptr<Knight> _target;
if (_target.expired() == false)
{
shared_ptr<Knight> sptr = _target.lock();
// ..
}
장점은 생명 주기에서 자유로워진다. weak_ptr 자체는 객체의 소멸 자체에는 영향을 주지 않기때문에 순환 구조가 일어날 수 없다.
단점은 expired()로 명시적으로 체크 후 shared_ptr로 다시 한번 전환하는 과정이 추가되기 때문에 번거로워진다.
상황에 맞춰서 shared_ptr만 사용할 것인지, weak_ptr도 병행해서 사용할 것인지 잘 고르면 된다.
unique_ptr
단 하나만 존재할 수 있다.
다른 곳에 넘겨줄때는 move를 통해 모두 이양해서 사용해야 한다.
일반적인 복사가 막히고 이동만 가능한 포인터라고 이해하면 된다.
make_unique<>()를 통해 초기화 하면 된다.
쌩으로 포인터를 사용하는 것은 더 이상 없다고 생각하면 된다.
'C++ > Rookiss C++' 카테고리의 다른 글
[Modern C++] 람다 (lambda) (0) | 2022.08.31 |
---|---|
[Modern C++] 전달 참조(forwarding reference) (0) | 2022.08.31 |
[Modern C++] 오른값 참조 (rvalue reference) (0) | 2022.08.31 |
[Modern C++] #2 (0) | 2022.08.31 |
[Modern C++] #1 (0) | 2022.08.31 |