2015년 10월 14일 수요일
[C++][Android] Smart Pointer
Smart Pointer
: 포인터처럼 동작하며 자동으로 메모리를 해제하고 안전하게 resource를 관리하도록 돕는 객체
포인터는 소멸자가 호출되지 않아 memory leak이 발생한다.
Java, C#같은 VM이 있는 언어는 VM에서 Garbage collector가 leak 이 발생하는 메모리를 해제시킨다.
C++에서도 자동으로 메모리를 해제하는 기법을 개발하게 되는데 이것이 smart pointer이다.
Sptr이 진짜 pointer라면 -> 연산자가 동작해야하는데 동작하지 않는다.
p.operator->()go(); 이렇게 처음에 바뀌지만 문법에 맞지 않아서 컴파일러는 아래와 같이 바꾼다.
(p.operator->())->go();
따라서 -> 연산자를 재정의해야 한다.
* 이것도 마찬가지로 operator로 제공되야 한다.
(p.operator*()).go(); 로 바뀌어서 호출될 것이다.
정리)
특정 객체가 임의의 포인터 타입이 되기 위해서는 아래의 두 연산자를 재정의 해야 한다.
*, ->
Smart pointer 특징)
진짜 포인터가 아니고 객체이므로 개발자가 생성/복사/대입/소멸의 과정을 직접 제어할 수 있다.
e.g, 소멸자를 활용한 자동 삭제
그리고 궁극적으로 Smart Pointer 로 활용되기 위해서는 어떤 object pointer 도 받아 들이기 위해서 template 을 이용해야 한다.
Smart pointer 의 복사 생성자
진짜 pointer 는 대입 연산자가 동작한다.
제대로 동작하지 않는다.
위 코드는 default 복사 생성자가 호출되어 얕은 복사가 진행됐기 때문에
소멸자에서 이미 해제된 메모리를 또 다시 해제하려고 할 것이다.
그렇다면, 얕은 복사 대신에 깊은 복사를 하는 복사 생성자를 만들면 해결이 될까?
하지만 Android Framework 에서는 객체 복사시에 깊은 복사 방식을 사용하지 않고
Reference counting(참조 계수) 방식으로 객체 복사를 구현하고 있다.
참조 계수 방식으로 Smart pointer 객체의 복사를 구현
왜 깊은 복사가 아닌 참조 계수 방식을 사용할까?
예를 들어, Smart pointer를 깊은 복사로 구현할 경우,
위 코드에서 p2에서 값을 수정할 경우 p1의 값이 변경되지 않는다
이것은 pointer 의 개념을 벗어나게 되고, 깊은 복사가 Smart pointer에 적용되지 않는 이유다.
이미 참조 계수 기반의 스마트 포인터는 C++ 표준에서 지원한다.
하지만 Android Framework 는 이 방식의 Reference counting 기법을 적용한 Smart Pointer 를 사용하지 않는다.
Reference counting 기법을 적용한 Smart pointer 의 문제점
Smart pointer가 처음에 의도했던(Pointer 같은 Object) 와 달리 sizeof 연산자를 사용하면
int* 와 다른 값을 나타낸다.
Reference counting 기법을 사용한 Smart pointer 는
참조 계수 기반으로 구현해야 하기 때문에, 포인터를 2개 가져가므로 8 byte 를 사용하게 된다.
그래서 Android Framework 에서는 표준에서 제공하는 Smart Pointer 를 사용하지 않는다.
해결책 :
reference count 을 Smart Pointer 에서 관리하지 않고 해당 object 에서 관리하도록 변경한다.
Reference count를 object 에서 관리하도록 변경
Smart Pointer 를 다시 되돌아 보면, Smart Pointer 의 장점은 객체의 생성/복사/대입/소멸을 직접 관리할 수 있다는 것이다.
그러므로, Smart Pointer 의 생성/소멸 시점에서 내부 객체(e.g, Car)의 참조 계수를 증가/감소 시키면 될 것이다.
더불어 Smart Pointer 는 내부 객체의 포인터만을 관리하게 됨으로써 궁극적으로 Pointer 그 자체로써 표현이 가능하게 된다.
결과적으로 개발자가 객체를 관리하는 것이 아니라, Smart Pointer 가 책임을 지게 되고
이렇게 위임된 코드 스타일을 proxy pattern 이라 한다.
하지만 객체의 포인터를 사용할 때, 매번 참조 계수를 inc/dec 하는 것은 매우 번거롭다.
그렇다면 Smart Pointer 의 생성/소멸에서 내부 객체의 참조 계수 증가/감소를 호출하는 방식을 살펴보자.
Smart Pointer 의 생성자,소멸자에서 내부 객체의 참조 계수 증가/감소를 호출하여
직접적으로 참조 계수 증가/감소를 개발자가 호출하지 않도록 했다.
이 방식을 적용한 Smart pointer는 진짜 pointer와 동일한 크기를 갖게 된다.
하지만 Android Framework 는 primitive type을 smart pointer로 지원하지 않는다.
primitive 는 mCount 같은 참조 계수가 내부 없기 때문이다.
결국, Android Framework 는 RefBase의 class 만을 Smart Pointer 로 지원한다.
정리 :
C++ 표준에서의 참조 계수 기반의 Smart Pointer 는 shared_ptr 이다.
shared_ptr
장점 : 모든 타입에 대하여 처리 가능
단점 : 메모리의 사용량 증가(reference count 를 내부에서 관리하므로)
Android 에서의 참고 계수 기반의 Smart Pointer 는 sp(strong pointer) 이다.
장점 : 메모리 사용량에 있어 overhead가 없다.(reference counting을 내부에서 관리하지 않음)
단점 : RefBase 클래스의 자식이 아닌 경우, Smart pointer로 처리될 수 없다.
sp : Smart Pointer 가 아니라 Strong Pointer로 불린다.
wp : weak pointer
상호 참조 - Reference counting 기법을 사용한 Smart pointer의 문제점
p1, p2 는 서로를 참조하므로 reference count 가 감소되지 않아 소멸자가 호출되지 않는다.
이런 상호 참조 문제점을 해결하기 위해,
상호 참조하는 경우 reference count 를 증가시키지 않도록 하는 방법이 제안되었다.
이것이 weak pointer 이며 C++ 표준으로 지원한다.
Weak pointer
Android 에서도 동일하게 WP(weak pointer)를 제공한다.
피드 구독하기:
댓글 (Atom)
[C++] meta programing
재귀 호출에 관해 template meta programming 을 적용한 예제를 살펴보자. #include using namespace std; int fact(int n){ if(n factorial 연산을 하는 일반적인 재귀 호출 함...
-
Smart Pointer : 포인터처럼 동작하며 자동으로 메모리를 해제하고 안전하게 resource를 관리하도록 돕는 객체 포인터는 소멸자가 호출되지 않아 memory leak이 발생한다. Java, C#같은 VM이 있는 언어는 VM에서 G...
-
nullptr 란? C++11 에서 지원하는 null pointer 상수 먼저 pointer 가 초기화될 수 있는 정수 값을 살펴보자. 정수 0은 모든 타입의 포인터에 암시적 형변환을 통해서 초기화 값으로 사용될 수 있다. 하지만 그 이...
-
자료 구조를 순회하여 데이터에 접근하는 방법을 살펴보자. Container 에 저장된 데이터에 접근하기 위해 대표적으로 Iterator의 begin(), end() function 을 사용한다. 하지만 위 예제에서 show() 함수에 배열이...
댓글 없음:
댓글 쓰기