/*------------------
RefCountable
------------------*/
// 최상위 객체에서 카운팅하고 나머지가 참조
class RefCountable
{
public:
RefCountable() : _refCount(1) {}
virtual ~RefCountable() {}
int32 GetRefCount() { return _refCount; }
int32 AddRef() { return ++_refCount; }
int32 ReleaseRef()
{
int32 refCount = --_refCount;
// 아무도 참조하고 있지 않음.
if (refCount == 0)
{
delete this;
}
return refCount;
}
protected:
atomic<int32> _refCount;
};
class Wraigth : public RefCountable
{
public:
int32 _hp=150;
int32 _x=0;
int32 _y=0;
};
class Missile : public RefCountable
{
public:
void SetTarget(Wraigth* target)
{
_target = target;
target->AddRef();
}
bool Update()
{
if (_target == nullptr)
return true;
int x = _target->_x;
int y = _target->_y;
// 타겟이 중간에 없어지면
// 카운트를 줄이고 nullptr로 밀어줌
// 만약 이 클래스만 참조하고 있었으면 _target이 가리키는 것은 메모리에서 해제 될 것
if (_target->_hp == 0)
{
_target->ReleaseRef();
_target = nullptr;
return true;
}
return false;
}
Wraigth* _target = nullptr;
};
int main()
{
//RefCount ver
Wraigth* wraight = new Wraigth();
Missile* missile = new Missile();
// SetTarget 내부에서 refCnt를 증가시킨다.
missile->SetTarget(wraight);
wraight->_hp = 0;
wraight->ReleaseRef();
wraight = nullptr;
while (true)
{
if (missile)
{
if(missile->Update())
{
missile->ReleaseRef();
missile = nullptr;
}
}
}
missile->ReleaseRef();
missile = nullptr;
/*------------------
SharedPtr
------------------*/
// RefCountable 객체를 래핑해서 들고 있도록
template<typename T>
class TSharedPtr
{
public:
TSharedPtr() {}
TSharedPtr(T* ptr) { Set(ptr); }
//복사
TSharedPtr(const TSharedPtr& rhs) { Set(rhs->_ptr); }
//이동
TSharedPtr(TSharedPtr&& rhs) { _ptr = rhs._ptr; rhs._ptr = nullptr; }
//상속 관계 복사
template<typename U>
TSharedPtr(const TSharedPtr<U>& rhs) { Set(static_cast<T*>(rhs._ptr)); }
~TSharedPtr() { Release(); }
public:
TSharedPtr& operator=(const TSharedPtr& rhs)
{
if (_ptr != rhs._ptr)
{
Release();
Set(rhs->_ptr);
}
return *this
}
TSharedPtr& operator=(TSharedPtr&& rhs)
{
Release();
_ptr = rhs._ptr;
rhs._ptr = nullptr;
}
bool operator==(const TSharedPtr& rhs) const { return _ptr == rhs._ptr; }
bool operator==(T* ptr) const { return _ptr == ptr };
bool operator!=(const TSharedPtr& rhs) const { return _ptr != rhs._ptr; }
bool operator!=(T* ptr) const { return _ptr != ptr };
bool operator<(const TSharedPtr& rhs) const { return _ptr < rhs._ptr; }
T* operator*() { return _ptr; }
const T* operator*() const { return _ptr; }
T* operator->() { return _ptr; }
const T* operator->() const { return _ptr; }
// 캐스팅
operator T* () const { return _ptr; }
bool IsNull() { return _ptr == nullptr; }
private:
inline void Set(T* ptr)
{
_ptr = ptr;
if (ptr)
ptr->AddRef();
}
inline void Release()
{
if (_ptr != nullptr)
{
_ptr->ReleaseRef();
_ptr = nullptr;
}
}
private:
T* _ptr = nullptr;
};
#include "RefCounting.h"
class Wraigth : public RefCountable
{
public:
int32 _hp=150;
int32 _x=0;
int32 _y=0;
};
//TShared Ver
using WraigthRef = TSharedPtr<Wraigth>;
using MissileRef = TSharedPtr<Missile>;
class Missile : public RefCountable
{
public:
//void SetTarget(Wraigth* target)
void SetTarget(WraigthRef target)
{
_target = target;
}
bool Update()
{
if (_target == nullptr)
return true;
int x = _target->_x;
int y = _target->_y;
if (_target->_hp == 0)
{
_target = nullptr;
return true;
}
return false;
}
WraigthRef _target = nullptr;
};
int main()
{
//TShared Ver
// 여기서 cnt가 2임
// new Wraigth()에서 1
// WraigthRef wraight() 에서 1
WraigthRef wraight(new Wraigth());
// 소유권을 스마트 포인터에게 넘김
wraight->ReleaseRef();
MissileRef missile(new Missile());
missile->ReleaseRef();
//여기서 넘겨줄 때 복사가 발생
//여기서 refcnt +1 하기 때문에
// 내가 사용하는 동안은 최소1, 삭제되지 않음
missile->SetTarget(wraight);
wraight->_hp = 0;
//이게 동작하는 이유
// wraight = WraigthRef(nullptr)
wraight = nullptr;
while (true)
{
if (missile)
{
if (missile->Update())
{
missile = nullptr;
}
}
}
missile = nullptr;
}
modern C++에서 smart pointer는 프로그래머로 하여금 메모리, 자원의 누수로부터 자유롭게 해주고 예외에 안전한지 확인하는 데 도움을 주는 포인터로 (출처) auto_ptr(더이상 사용되지 않음), unique_ptr, shared_ptr, weak_ptr이 있다.
#include <iostream>
#include <memoty>
using namespace std;
class Knight
{
public:
Knight()
{
cout << "Knight()" << endl;
}
~Knight()
{
cout << "~Knight()" << endl;
}
};
int main()
{
/*------------------
unique_ptr
------------------*/
//일반 포인터와 유사 but 복사가 막혀있음
//자동 해제 해줌
unique_ptr<Knight> k2 = make_unique<Knight>();
//unique_ptr<Knight> k3 = k2; //err
unique_ptr<Knight> k3 = std::move(k2); //ok
/*-------------------
shared_ptr & weak
-------------------*/
// weak의 경우 순환 해결 가능
// [Knight][RefCountingBlock] (만들어진 메모리)
// spr과 spr2가 동일한 메모리를 참조함
// [T*][RefCountingBlock*] (메모리를 가리키는 포인터)
shared_ptr<Knight> spr(new Knight());
// [T*][RefCountingBlock*] (메모리를 가리키는 포인터)
shared_ptr<Knight> spr2 = spr;
// 위의 방법은 2개의 메모리를 따로따로 할당
// 이 방법은 메모리를 한번에 할당
shared_ptr<Knight> spr3 = make_shared<Knight>();
//RefCountingBlock (use, weak로 구성)
//use shared로 참조하는 카운트
//weak weak로 참조하는 카운트 (이게 0이 아니면 RefCountingBlock 계속 살아 있음)
weak_ptr<Knight> wpr = spr3;
// 자신이 유효한지 확인 후 사용
// 둘중 하나를 사용
bool expired = wpr.expired();
shared_ptr<Knight> spr4 = wpr.lock();
}