[C++] 스마트포인터(Smart Pointer)
2015. 7. 14. 14:09ㆍ프로그래밍/C/C++
728x90
728x90
RAII
- RAII는 C++에서 자주 쓰이는 idiom으로 자원의 안전한 사용을 위해 객체가 쓰이는 스코프를 벗어나면 자원을 해제해주는 기법이다.
- C++에서 heap에 할당된 자원은 명시적으로 해제하지 않으면 해제되지 않지만, stack에 할당된 자원은 자신의 scope가 끝나면 메모리가 해제되며 destructor가 불린다는 원리를 이용한 것이다.
스마트 포인터 정의
- 자원관리 기법에 사용되며 메모리 자원 관리와 경계 검사 등
- 기존 포인터 변수에서 기능이 추가 된 추상 데이터 타입.
스마트 포인터의 특징
- 자원을 획득한후, 자원 관리 객체에게 넘긴다
- 자원관리 객체는 자신의 소멸자를 사용하여 자원이 확실하게 해제 되도록 한다
- 버그 보완(안전), 자동청소, 자동 초기화를 해준다
- 스마트 포인터는 C++에서 안전하고 효율적인 코드를 작성하는데 유용하다
- delete를 이용해 직접 메모리를 해제하지 않고, 스마트 포인터의 소멸자에 존재하는 delete 키워드를 이용해서 메모리를 삭제한다
- 원본 삭제 및 참조 카운팅 등을 이용해 댕글링 포인터가 되는 것을 막는다
스마트 포인터의 장점
- Dangling Pointer로 인해 메모리 누수 현상을 방지할 수 있다.
Dangling Pointer란?
- 첫 번째 - delete로 해제된 메모리를 가리키는 포인터.
#include "stdafx.h"
#include <iostream>
void func(int* ptr);
int _tmain(int argc, _TCHAR* argv[])
{
int* int_ptr = nullptr;
func(int_ptr);
printf("%d", *int_ptr);
return 0;
}
void func(int* ptr)
{
int dangling = 10;
ptr = &dangling;
}
- 두 번째 - 스택 지역에서 사라진 메모리를 가리키는 포인터.
#include "stdafx.h"
#include <iostream>
void func(int* ptr);
int _tmain(int argc, _TCHAR* argv[])
{
int* int_ptr = nullptr;
int_ptr = new int(10);
delete int_ptr;
// dangling
printf("%d", *int_ptr);
return 0;
}
사용하는 이유
코드 간략화
자동 해제
자동 초기화
Dangling Pointer 방지
Exception 안전
- 정상적인 함수 종료에 의한 함수 종료든 Exception이 발생하였든 소멸자는 항상 호출된다.
가비지 컬렉션
- 일부 언어들은 자동 가비지 컬렉션 기능을 제공하지만 C++은 그렇지 못하다.
- 스마트 포인터는 가비지 컬렉션 용도로 사용할 수 있다.
효율성
- 스마트 포인터는 가용한 메모리를 좀 더 효율적으로 사용할 수 있게 하며 할당, 해제 시간을 단축 시킬 수 있다.
- COW( Copry On Write ) : 1개의 객체가 수정되지 않는 동안 여러 COW 포인터가 해당 객체를 가리킬 수 있도록 하되, 해당 객체가 수정되는 경우 COW 포인터가 객체를 복사 한 후 복사 본을 수정하는 방법
- 객체가 할당되거나 운용되는 환경에 대해 일부 가정을 세울 수 있는 경우 최적화된 할당 계획이 가능하다.(운영 체제나 응용 프로그램이 변경된다 하더라도 클래스의 코드를 최적화된 할당 계획을 만들 수 있다.)
스마트 포인터의 단점
- 스마트 포인터가 NULL 인지 체크 불가
- 상속 기반의 변환 제한
- 상수 객체에 대한 포인터 지원 제한
- 구현하기 까다롭다.
- 이해하기 쉽지 않아 유지보수도 어렵다.
- 디버깅이 어렵다.
스마트 포인터의 종류
- STL 라이브러리에 공식직원하는 스마트 포인터는
- auto_ptr, shared_ptr, unique_ptr, weak_ptr이 있다.
- 그리고 부스터 라이브러리에서 제공하는 scoped_ptr, shared_array 등이 있다.
auto_ptr
- 정의: 포인터 변수와 비슷하게 동작하는 객체로써, 가리키고 있는 대상(동적 할당 된 대상)에 대해 auto_ptr 클래스의 소멸자가 자동으로 delete를 호출하는 클래스이다.
- 특징
- 복사 시 소멸식 복사를 하기 때문에 소유권을 이전한다. 그러므로 컨테이너에 절대 넣으면 안 된다.
- 내재된 포인터에 대해 오직 하나만의 소유만을 허용한다.
- C++11에 이르러 표준에서 제외되었으며 유사한 기능성에, 보다 안정적인 unique_ptr로 대체되었다.
- 어떠한 자원을 가리키는 auto_ptr의 개수가 둘 이상이면 절대로 안되기 때문에, auto_ptr 객체를 복사하면(복사 생성자 혹은 복사 대입 연산자를 통해) 원본 객체가 가리키는 값을 null로 만든다. 즉, 복사된(copying) 객체만이 그 자원 의 유일한 소유권(ownership)을 갖는다.
- 소유권이 옮겨지면 일반 포인터와 다르게 null 포인터가 된다.
shared_ptr
- 정의: 참조 카운팅 방식 스마트 포인터( RCSP : Reference-Counting Smart Pointer )이다.
- 가리키고 있는 객체에 대해 소유권을 가지지 않고 참조 카운트만 유지하는 포인터이다.
- 특징
- 다른 객체를 가리키면서 포인터에 대한 소유권을 공유할 수 있다(aliasing). 해당 포인터에 대한 소유권을 가지지 않고 참조 카운팅만 유지하는 방식이다.
- 자원을 참조하는 외부 객체의 개수를 관리하고 있다가, 그 개수가 0이 되면 해당 자원을 자동으로 삭제한다.
- STL 컨테이너 사용이 가능하다.
- 가비지 컬렉션( Garbage Collection )의 동작과 흡사하지만, 가비지 컬렉션과는 달리 참조 상태가 고리를 이루는 경우( 서로 다른 두 객체가 서로를 참조하는 경우 )는 없앨 수 없다.
unique_ptr
- 정의: C++11에 이르러 포함되었으며 auto_ptr을 보다 개선한 형태(배열 삭제)를 지원한다.
- 특징
- 소유권 독점 방식을 사용하는 스마트 포인터로, auto_ptr과 유사하나 복사 생성자와 대입 연산자가 제공되지 않는다.
- 복사 생성자와 대입 연산자를 제공하지 않기 때문에 복사를 할 수 없으며, 소유권 이전을 위해서 std::move()를 사용한다.
- 소유권 해제 시점은 포인터 객체가 가리키는 객체의 소멸, 대입연산, reset 메소드에서 명시적 호출을 통해 값을 변경할 때 이다.
- 소유권을 독점하는 방식이기 때문에 auto_ptr은 객체에 대한 포인터가 유일하도록 관리해주었어야 하지만, unique_ptr은 스스로 포인터의 유일성을 보장한다.
weak_ptr
- 정의: shared_ptr의 순환 참조로 발생하는 문제를 해결하기 위해 사용하는 특수한 포인터이다.
- 특징
- weak_ptr 은 shared_ptr을 관리하기 위한 reference count에 포함되지 않고 shared_ptr의 객체를 참조만 한다. ( shared_ptr 의 weak reference count로 관리)
- 약한 참조 성질을 지닌다.( 객체가 살아 있도록 유지 시키지 않고, 단순히 객체가 살아 있는 동안만 참조 가능 )
- expired 함수를 통해 자신이 참조하고 있는 shared_ptr의 상태를 확인 할 수 있다.
- weak_ptr 객체는 포인터에 대해 직접 접근이 불가능 하며, 액세스를 원하면 lock 메소드를 통해 shared_ptr로 변환한 뒤, shared_ptr 의 get 메소드를 사용한다
728x90
반응형
'프로그래밍 > C/C++' 카테고리의 다른 글
[C/C++] 삼항 연산자 (4) | 2015.11.27 |
---|---|
[C++11] 주요 기능들 (0) | 2015.09.07 |
[C++/STL] STL 벡터와 배열 차이 (1) | 2015.08.19 |
[STL] vector의 at()과 []의 차이 (1) | 2015.07.11 |
2차원 벡터 사용하기 (1) | 2015.07.08 |
비트 연산으로 정수에서 이진수 0,1 뽑아내기 (0) | 2015.07.08 |
[C++] 가상 함수 테이블 (0) | 2015.04.01 |