언리얼에서 제공하는 자동 메모리 관리 시스템, 가비지 컬렉션을 보면서 참조 카운팅이란 개념에 집중하다 C에서도 이를 이용해 반자동적으로 메모리 관리에 도움을 주는 스마트 포인터가 생각이 났다.
개인적으로 찾으며 알게된 조금 더 깊은 내용도 포함해 스마트 포인터에 대한 내용을 기록한다.
new, delete로 할당한 메모리를 해제해야 하는 것은 C++ 프로그래머로서 중요한 부분이다.
자칫하면 큰 문제가 발생할 수 있는 문제와 직결되는 간단한 문제이기 때문이다.
이때문에 Effective C++에서도 「클래스를 통한 메모리 생성과 해제에 대한 부분은 생성자와 소멸자에서 처리하게 만듦 = RAII」으로써 프로그래머가 메모리 관련 문제에 쓰이는 신경을 해소하는 테크닉을 소개한 기억이 난다.
나는 스마트 포인터는 포인터가 아니라 이런 테크닉을 기반으로 템플릿으로 작성해 어느 부분에서도 메모리 생성, 해제 동작을 생성자와 소멸자에 인계해 관리하는 기법이라 이해했다.
실제로 C++을 소개하는 어느 페이지에서 스마트 포인터를 "포인터 처럼 사용하는 클래스 템플릿으로 메모리를 자동으로 해제해 준다"라고 소개하고 다음과 같은 코드를 예시로 보여준다.
class Point
{
int x, y;
public:
Point() : x(0), y(0) {};
Point(int X, int Y) : x(X), y(Y) {};
...
};
class SmartPointer
{
Point* ptr;
public:
SmartPointer(Point* p) : ptr(p) {}
~SmartPtr()
{
delete ptr;
}
Point* operater->() const
{
return ptr;
}
}
이렇게 만들어진 템플릿 클래스, 스마트 포인터의 종류를 먼저 작성하는게 순서겠다.
어떤 하나의 객체를 참조하는 스마트 포인터의 개수를 참조하는 스마트 포인터다. 참조하고 있는 스마트 포인터의 개수를 해당 메모리를 참조하는 포인터가 몇개인지 나타내는 값, 참조 카운트라 칭하고 shared_ptr가 추가될 떄 +1, 삭제되면 -1한다. 이 참조 카운터가 0이 되면 메로리를 해제한다.
shared_ptr의 의의는 객체를 가리키는 스마트 포인터의 개수를 관리, 확인 할 수 있다는 것이다. 다만 불필요한 참조 카운트를 늘리는 행동은 성능 저하를 부르기 때문에 적절한 사용이 필요하다.
하나의 스마트 포인터만 객체를 가리킬수 있게한 스마트 포인터다. 즉, 참조 카운터가 1을 넘을 수 없다.
unique_ptr의 의의는 소유권 개념을 도입한 스마트 포인터라는 건데, "소유권을 이전" 하는 행동으로 다른 객체가 같은 메모리 주소를 사용하는 것을 방지해 메모리 관리 시점에서 안전하고 예측 가능하게 만든다.
unique_ptr<int> ptr1(new int(5));
unique_ptr<int> ptr2 = move(ptr1);
동적 메모리에 대한 독점적인 소유권을 가질 수 있다는 건 unique_ptr 객체가 같은 메모리를 소유하는걸 허용하지 않는다는 말고 같다. 이런 현상은 unique_ptr이 복사 생성자와 복사 대입연산자를 삭제하기 때문이다.
이 점을 이용해 함수에서 동적으로 메모리를 할당할 때, unique_ptr을 사용해 반환될 메모리 소유권을 보장해 함수에서 벗어날 때 삭제될 메모리가 복사되는 동작을 방지할 수 있다.
shared_ptr과 자주 같이 쓰이는 스마트 포인터로 순환 참조 같은 문제 해결에 도움이 된다.
shared_ptr과 유사하지만 가리키는 객체의 수명에 영향을 주지 않는 일명 '약한 참조'를 제공한다.
shared_ptr<class A> a(new A());
shared_ptr<class B> b(new B());
a->b_ptr = b;
b->a_ptr = a;
같은 형태는 A,B 가 서로를 참조해 참조 카운터가 절대 0이 되지않아 메모리 누수가 발생한다.
이런 경우를 weak_ptr이 해결 해주는데 weak_ptr로 생성한 객체는 파괴되면 자동으로 nullptr로 설정된다.
또 lock() 함수를 통해 shared_ptr로 변경할 수도 있다.
대충 기본 개념만 봐도 스마트 포인터가 메모리 관리라는 점에서 굉장히 많은 도움이 되는걸 알 수 있다.
그외에도 더 많은 장점을 적어본다면
인데, 모두 메모리 관리 뿐만 아니라 안정성 또한 얻을 수 있다.
[실수] 부동 소수점 (0) | 2024.08.27 |
---|---|
Cast ~ static / dynamic의 소모 비용 (0) | 2024.03.25 |
이동 생성자의 원리 w.우측값(rvalue) (0) | 2024.03.06 |
[이론] 상속의 규칙과 사용에 따른 가상함수 비용 (0) | 2024.02.19 |
malloc/free 와 new/delete의 차이점 (1) | 2023.10.12 |