개요
Reference Counting은 객체의 소유권 관리( = 라이프 사이클 )의 방법 중 하나로 객체를 참조(포인팅) 하고 있는
횟수를 추적하여 그 횟수가 0이 되면 메모리에서 해제(소멸)한다.
대부분의 Managed Language (python, c#, swift등 메모리 관리를 직접 하지 않는 언어 ) 에서 널리 사용되고 있다.
Unmanaged Language (c, c++등) 에서도 결국 객체 관리를 위해 (또한 프로그래머의 실수를 줄이기 위해)
Reference Counting을 구현하여 사용하고 있다...
장점
- 메모리를 직접 해제하는 번거로움이 사라진다. (잘가라 delete)
- 객체의 소유권을 공유할 수 있다
- 빠르다. (객체 관리 매커니즘이 비교적 단순하다. feat. Garbage Collection...)
단점
- 순환 참조 문제가 있다.
구현
c++ 에서의 구현방식에는 크게 두가지가 있다.
Intrusive Reference Counting (침습성 참조 카운팅)
- 객체에 대한 참조 카운트가 "내장" 되어있다.
- 참조 카운트 매커니즘을 객체가 제공해야한다.
- 메모리사용은 보통 "비침습성"에 비해 작다.
- object 해제시 count 정보도 날아가기 때문에 약한 참조를 구현할 수 없다. (=> 순환참조 문제)
!주의 : 이해를 돕기 위한 코드로 현업에서 사용시 뚝배기가 깨집니다!!
#include <iostream>
class MyObject
{
public:
MyObject(void) : _refCount(0) { std::cout << ":: Initailized!" << std::endl; }
~MyObject(void) { std::cout << ":: Deinitialized!" << std::endl; }
int getCount(void)
{
return _refCount;
}
void addCount(void)
{
++_refCount;
}
void releaseCount(void)
{
if (0 >= --_refCount)
{
delete this;
}
}
private:
int _refCount;
};
template<class Type>
class RefCounted
{
public:
RefCounted(Type* pObject)
: _pObject(pObject)
{
_pObject->addCount();
}
RefCounted(RefCounted<Type>& pObject)
: _pObject(pObject.get())
{
_pObject->addCount();
}
~RefCounted(void)
{
reset();
}
Type* get(void)
{
return _pObject;
}
private:
void reset(void)
{
_pObject->releaseCount();
}
private:
Type* _pObject;
};
int main(void)
{
RefCounted<MyObject> count1(new MyObject);
std::cout << "count1 : " << count1.get()->getCount() << std::endl;
RefCounted<MyObject> count2(count1);
std::cout << "count1 : " << count1.get()->getCount() << std::endl;
std::cout << "count2 : " << count2.get()->getCount() << std::endl;
return 0;
}
결과:
===========================================
:: Initailized!
count1 : 1
count1 : 2
count2 : 2
:: Deinitialized!
===========================================
non-Intrusive Reference Counting (비침습성 참조 카운팅; c++의 shared_ptr)
- 객체에 대한 참조 카운트를 따로 관리한다.
- 참조 카운트에 대한 포인터가 추가되어 보통 메모리사용이 "침습성"에 비해 크다
- count 정보가 따로 있어 약한 참조를 구현할 수 있다.
!주의 : 이해를 돕기 위한 코드로 현업에서 사용시 뚝배기가 깨집니다!!
#include <iostream>
class MyObject
{
public:
MyObject(void) { std::cout << ":: Initailized!" << std::endl; }
~MyObject(void) { std::cout << ":: Deinitialized!" << std::endl; }
};
template<class Type>
class RefCounted
{
public:
RefCounted(Type* pObject)
: _pObject(pObject)
, _pRefCount(nullptr)
{
addCount();
}
RefCounted(RefCounted<Type>& pObject)
: _pObject(pObject.get())
, _pRefCount(pObject._pRefCount)
{
addCount();
}
~RefCounted(void)
{
reset();
}
Type* get(void)
{
return _pObject;
}
int getCount(void) const
{
return *_pRefCount;
}
private:
void reset(void)
{
if (0 >= releaseCount())
{
delete _pObject;
delete _pRefCount;
}
}
void addCount(void)
{
if (nullptr == _pRefCount)
{
_pRefCount = new int;
(*_pRefCount) = 0;
}
++(*_pRefCount);
}
int releaseCount(void)
{
return --(*_pRefCount);
}
private:
Type* _pObject;
int* _pRefCount;
};
int main(void)
{
RefCounted<MyObject> count1(new MyObject);
std::cout << "count1 : " << count1.getCount() << std::endl;
RefCounted<MyObject> count2(count1);
std::cout << "count1 : " << count1.getCount() << std::endl;
std::cout << "count2 : " << count2.getCount() << std::endl;
return 0;
}
결과:
===========================================
:: Initailized!
count1 : 1
count1 : 2
count2 : 2
:: Deinitialized!
===========================================
결론
우리의 C++을 모던하게 쓰자! 적극적으로 shared_ptr을 쓰자!
'프로그래밍 > C++' 카테고리의 다른 글
Factory Pattern - Simple Factory (1) | 2021.02.21 |
---|---|
[C++] Cyclic Reference - Weak Reference (0) | 2020.11.11 |
힙(Heap)과 완전 이진 트리(Complete binary tree) (1) | 2019.10.31 |
[c] SHA-1 구조 및 코드 (2) | 2017.12.09 |
[c] AES 구조 및 코드 (2) | 2017.11.15 |