스마트 포인터의 Point!
C++11부터 도입되었으며 메모리 누수(memory leak)로부터 프로그램의 안전성을 보장하기 위해 만들어졌다.
C++의 RAII 디자인패턴 기반.
C++의 장점이자 단점은 포인터를 통해 직접적으로 메모리에 접근하는 것
{
Foo foo;
} // scope
{
Foo *foo = new Foo; // 힙에 할당
}
std::lock_guard<mutex>
활용#include <memory>
void foo(){
std::shared_ptr<bool> p_bool = std::make_shared<bool>();
}
/* 참조 카운트를 관리하는 블록 클래스 */
class RefCountBlock {
public:
int refCount = 1;
}
/* Shared 스마트 포인터 슈도코드 */
template<typename T>
class SharedPtr {
public:
SharedPtr() {} // 사실상 널 포인터 생성
SharedPtr(T* ptr)
: _ptr(ptr)
{
_block = new RefCountBlock();
// SharedPtr이라는 래퍼를 활용해,
// 메모리라는 리소스 획득을 초기화 과정에서 이루어지게 함
}
~SharedPtr() {
if(_ptr != nullptr) {
_block->refCount--;
if(_block->refCount == 0){
// 실질적으로 관리하고 있던 할당 공간 반환
delete _ptr;
delete _block;
}
}
}
// (shared_ptr 특징)
// 소유권 대입->복사, 복사 생성자 활용
SharedPtr(const SharedPtr& sptr)
: _ptr(sptr._ptr), _block(sptr.block)
{
if(_ptr != nullptr){
// 나도 관리하는 공간에 대한 소유권을 주장하겠다!
_block->refCount++;
}
}
// "" => 복사 대입 연산자 활용
void operator=(const SharedPtr& sptr) {
_ptr = sptr._ptr;
_block = sptr._block;
if(_ptr != nullptr){
// 나도 관리하는 공간에 대한 소유권을 주장하겠다!
_block->refCount++;
}
}
private:
// 원본 포인터
T* _ptr = nullptr;
// 참조카운트 객체
RefCountBlock* _block = nullptr;
}
shared_ptr<AAA> a1 = make_shared<AAA>();
{
shared_ptr<AAA> a2 = make_shared<AAA>();
a1->ptr_target = a2;
a2->ptr_target = a1;
}
// 블록을 빠져나왔을 때 a1에 대한 refCnt는 2, a2에 대한 refCnt는 1
// 만약 a1에 대한 포인터도 더이상 접근될 수 없을 때, 각 refCnt가 1로 남아있어 메모리 반환이 되지 않는 문제가 발생할 수 있다.
// 이를 순환 참조에 의해 일으켜진 문제라하여 순환 참조 문제라 한다.
class RefCountBlock {
public:
int refCount = 1;
/* 추가 */
int weakCount = 1; // weak 포인터가 참조하는 수
}
/* Weak 스마트 포인터 슈도코드 */
template<typename T>
class WeakPtr {
public:
WeakPtr() {} // 사실상 널 포인터 생성
WeakPtr(T* ptr)
: _ptr(ptr)
{
_block = new RefCountBlock();
}
~WeakPtr() {
if(_ptr != nullptr) {
_block->refCount--;
if(_block->refCount == 0){
// 실질적으로 관리하고 있던 할당 공간 반환
delete _ptr;
//delete _block;
// weak ptr에서는 refCnt가 0일 때,
// 곧 바로 refBlock을 삭제하지 않는다.
// 해당 메모리가 날라갔는지 날라가지 않았는지 확인하는 용도로 사용할 수 있다.
// shared_ptr처럼 생명주기에 직접적으로 관여하지는 않음
}
}
}
...
}
expired()
함수를 통해 체크하고, 다시 shared_ptr로 관리해야 하는 번거러움이 생긴다.#include <memory>
void foo(){
std::unique_ptr<bool> p_bool = std::make_unique<bool>();
//std::unique_ptr<bool> p_bool2 = p_bool; // 대입 및 복사 불가
// 이동 가능
std::unique_ptr<bool> p_bool2 = std::move(p_bool);
}
참조
- https://medium.com/newworld-kim/%EB%AA%A8%EB%8D%98-c-%EC%8A%A4%EB%A7%88%ED%8A%B8-%ED%8F%AC%EC%9D%B8%ED%84%B0%EC%99%80-%EA%B0%9D%EC%B2%B4-%EC%83%9D%EC%84%B1-f3e820ea71f1
- https://hmjo.tistory.com/202
- https://blog.koriel.kr/cpp11-smart-pointer/
- https://musket-ade.tistory.com/entry/C-%EC%8A%A4%EB%A7%88%ED%8A%B8-%ED%8F%AC%EC%9D%B8%ED%84%B0-Smart-Pointer