본문 바로가기
메모리관리(스마트포인터...)

스마트포인터 (weak_ptr, unique_ptr, share_ptr)

by kcj3054 2022. 4. 7.

RefCounting부분

#include "pch.h"
#include <iostream>
#include "CorePch.h"
#include <atomic>
#include <mutex>
#include <windows.h>
#include <future>
#include "ThreadManager.h"

#include "RefCounting.h"

#pragma once

/*---------------
   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;
};

/*---------------
   SharedPtr
----------------*/

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;
        return *this;
    }

    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; }
    operator T* () const { return _ptr; }
    T* operator->() { return _ptr; }
    const T* operator->() 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;
};

Knight class


using KnightRef = TSharedPtr<class Knight>;

class Knight : public RefCountable
{
public:
    Knight()
    {
        cout << "Knight()" << endl;
    }
    ~Knight()
    {
        cout << "~Knight()" << endl;
    }
    void SetTarget(KnightRef target)
    {
        _target = target;
    }
public:
    KnightRef _target = nullptr;
};

오류가 발생하지 않는 상황

int main()
{
    KnightRef k1(new Knight());
    k1->ReleaseRef();

    KnightRef k2(new Knight());
    k2->ReleaseRef();

    k1->SetTarget(k2);

    k1 = nullptr;
    k2 = nullptr;

    return 0;
}
  • 여기서는 k1이 nullptr로 되면 가지고 있던 k2도 하나 사라지면서 오류 없이 작동한다.

순환이 발생하는 순간

KnightRef k1(new Knight());
    k1->ReleaseRef();

    KnightRef k2(new Knight());
    k2->ReleaseRef();

    k1->SetTarget(k2);
    k2->SetTarget(k1);

    k1 = nullptr;
    k2 = nullptr;
  • 이상황에서는 k1이 맴버변수로 k2까지 들고있고 k2도 k1을 맴버변수로 들고 있어서
int32 ReleaseRef()
    {
        int32 refCount = --_refCount;
        if (refCount == 0)
        {
            delete this;
        }
        return refCount;
    }
  • ReleaseRef부분에서 사용을 하지않아도 refCount가 0이 되지않아서 delete가 되지않는다 순환 문제가 발생하는 것이다. (데드락과 비슷한..) 그래서
k1->SetTarget(nullptr);
    k2->SetTarget(nullptr);
  • 위 부분처럼 주시대상을 nullptr로 만드는 작업도 해야한다.

unique_ptr

  • unique_ptr은 간단하다, 하나의 독자전인 포인터를 들고있겠다는 것이고 또한 복사가 금지 되어있어서 복사 대신 move로 해야한다
//unique_ptr -> 복사가 금지되어있다.
    unique_ptr<Knight> k2 = make_unique<Knight>();
    unique_ptr<Knight> k3 = std::move(k2);

  • 해당영역이 복사가 금지되어있는 것이다 unique_ptr의 내부 자료

shared_ptr

  • shared_ptr내부를 보면 _Ptr_base를 상속 받고 있다. _Ptr_base는 맴버 변수 (참고로 _Ptr_base는 shared_ptr와 weak_ptr모두 상속 받고있다)
private:
    element_type* _Ptr{nullptr};
    _Ref_count_base* _Rep{nullptr};
  • 위의 2가지를 가지고있다. [T][RefCountingBlock] 이라고 생각하면된다. 내가 가리키고 있는 포인터랑, 참조하고있는 RefCounting수이다.

shared_ptr weak_ptr이 상속받고있는 ptr_base에서 가지고 있는 Refcount

  • RefCount를 보면 uses랑 weaks를 가지고 있다.

  • [Knight][RefCountingBlock(uses, weak두가지가 있다)]

  • shared_ptr는 다 사용했으면 uses를 감소하고, Kinght를 해제하는데 weak_ptr은 RefCountingBlock에만 관여를 하고 바라보는 대상에는 상관하지않는다.. 그래서 순환문제를 막을 수 있지만 weak_ptr을 사용할려면 expired로 있는지 체크를 한후 사용하는 것이 번거롭다.

bool expired = wpr.expired();

    shared_ptr<Knight> spr3 = wpr.lock();
    if (spr3 != nullptr)
    {

    }

'메모리관리(스마트포인터...)' 카테고리의 다른 글

stompAllocator, 가상메모리  (0) 2022.04.10
stompAllocator  (0) 2022.04.07
Allocator  (0) 2022.04.07
ReferenceCounting(스마트포인에 전..)  (0) 2022.03.31