C++

[C++] 유니크 포인터 unique_ptr

스누징어 2022. 7. 10. 17:21

유니크 포인터 unique_ptr

스마트 포인터란?

* 유니크 포인터는 스마트 포인터의 한 종류이다.

 

* 스마트 포인터란 포인터처럼 동작하는 클래스 템플릿이다.

 

* 스마트 포인터는 

  unipue_ptr, shared_ptr, weak_ptr 등이 있다.

 

 

기존의 포인터

Dog* dog = new Dog(10, "까망이");
delete dog;

* C++에서 new 연산자를 통해 heap 영역에 생성된 객체포인터를 가지고 접근해야 한다.

  객체를 삭제하고 싶으면 포인터를 사용해 직접 메모리를 해제해야 한다.

 

* 스마트 포인터를 사용하면 직접 delete를 할 필요 없이

  알아서 메모리를 해제한다. 메모리 누수가 일어날 확률이 적어지며,

  가비지 컬렉션보다 빠르다.

 

* 스마트 포인터와 비교하기 위해

  기존 포인터를 원시 포인터(raw pointer, naked pointer)라고 부른다.

 

 

유니크 포인터란?

* template< class T, class Deleter = std::default_delete<T> > class unique_ptr;

  클래스 템플릿인데 포인터의 역할을 수행한다.

  포인터처럼 동작하기 위한 연산자 오버로딩이 포함되어 있다.

 

* 유니크 포인터는 포인터가 단 하나라는 의미이다.

  포인터가 유일하므로 메모리를 해제하는 책임자가 명확하다.

 

* 유일하기 때문에 복사, 대입이 불가능하다. (컴파일 에러)

  원시 포인터를 공유하지 않는다.

 

* unique_ptr가 범위를 벗어나면 원시 포인터가 가리키는 메모리가 자동으로 해제된다.

  unique_ptr도 객체이기 때문에 소멸자를 가지고 있고,

  이 소멸자가 원시 포인터가 가리키는 메모리를 해제하는 방식이다.

 

* unique_ptr가 stack에 생성된 객체라면 범위만 벗어나도 소멸자가 호출되고,

  heap에 생성된 객체라면 delete를 해야 소멸자가 호출되어

  원시 포인터가 가리키는 메모리를 해제한다.

 

 

예시 (C++11 기준)

#include <memory>

class Dog :public Animal {
public:
	~Dog() { cout << "Dog 소멸자 호출됨" << endl; }
	void speak() { cout << "멍멍" << endl; }
};

int main() {
	unique_ptr<Dog> dog(new Dog(10, "까망이"));
	dog->speak();
	return 0;
}

* include <memory>를 해야 사용할 수 있다.

 

* dog->speak(); 를 보면

  객체를 마치 포인터처럼 사용한다. (연산자 오버로딩)

 

* 유니크 포인터 객체가 범위를 벗어나 소멸자가 호출되고,

  Dog 객체를 delete 한다.

 

 

원시 포인터가 가리키는 객체 삭제 기준

* 유니크 포인터 안에 있는 삭제자를 사용해

  원시 포인터가 가리키는 객체를 삭제하는 기준이 있다.

 

1. 유니크 포인터가 삭제될 때.

2. 연산자=reset()을 사용해 다른 포인터가 할당될 때.

 

* 연산자=로 nullptr를 넣거나 reset() 함수에 매개변수가 없다면

  원시 포인터가 null이 되고, 가리키는 객체를 delete 한다.

 

 

 

 

C++11에서의 문제

int main() {
	Dog* ptr = new Dog(10, "까망이");
	unique_ptr<Dog> dog1(ptr);
	unique_ptr<Dog> dog2(ptr);
	return 0;
}

* 유니크 포인터는 전제가 

  (나만이 유일하게 소유하는 중)인데,

  매개변수로 객체 포인터가 들어간다.

 

* 같은 포인터에 유니크 포인터 객체가 여러 개 생겨서

  이미 해제된 메모리 주소를 다시 해제하는 일이 생겼고,

  에러가 났다.

 

* 즉, 밖에서 객체를 생성하고 유니크 포인터에 나중에 넣는 방식을 허용했다.

  유일성을 보장받지 못하니 사용하지 말 것을 권장한다.

 

 

C++14에 나온 방식

* 이 방식으로 유니크 포인터 객체를 생성하기를 권장한다.

unique_ptr<Dog> dog = make_unique<Dog>(10, "까망이");

 

* new 키워드를 명시적으로 사용하지 않는다.

  내부적으로 매개변수를 사용해 new를 호출한다.

 

* make_unique<>()는 매개변수에 포인터를 없다.

  즉, 원시 포인터를 얻을 수 있는 방법이 없기 때문에

  중복 사용을 막을 수 있다.

 

* make_unique<>() 사용을 권장한다.

 

 

 

 

 

 

 

 

 

 

귀여운 그림은 낡은 창고님이 그리셨습니다.

반응형