블로그를 너무 오래 쉰 듯하다.. 다시 열심히 해야지
다시 시작하면서 포스팅할 주제는 스마트 포인터이다.
현재 개발하고 있는 시스템은 10년이상 전부터 개발된 것들이라 raw 포인터가 많지만 최근에 개발되는 코드에서는 스마트 포인터를 사용하는 추세인 것 같다.
C++ 11부터 도입되었다는데 왜 잘 사용되지 않았는지는 모르겠다.. 워낙 legacy가 많아서 그런걸까
최근에는 기존 기능을 수정하는 업무를 주로 하다보니 새로운 포인터를 다룰 일이 없었다.
다음에 기회가 되면 스마트 포인터를 사용해봐야겠다.
아무튼 아무래도 스마트 포인터를 사용하는 가장 큰 이유는 memory leak 방지라고 생각된다.
본인이 개발하는 시스템에서도 trouble report에서 memory leak이 심심찮게 발견되고 있다.
일단 첫 번째 스마트 포인터는 memory 헤더 파일에 정의되어 있다.
c++11부터 다음 세 가지의 스마트 포인터를 제공하고 있다.
1. unique_ptr
2. shared_ptr
3. weak_ptr

한 메모리를 바라보는 포인터들이 여러개인 경우 중간에 접근하는 포인터에서 메모리를 해제하는 경우 null 포인터 접근이 발생할 수도 있고 해제하지 않는 경우 memory leak이 발생할 수도 있다. raw 포인터는 이를 직접 delete를 사용해서 관리해야한다.
업무 중에 포인터를 전달하고 전달하다가 마지막에 delete를 하는 코드를 본적이 있다. 지금 생각해보면 비효율적이다..
추후에 멀티쓰레드 환경에서는 어떤 식으로 포인터를 관리하는지 찾아봐야겠다.
그리고 참조한 블로그에서 언급한 예로 예외등이 발생해서 delete 코드가 실행되지 않는 경우...
raw 포인터는 모든 예외 처리 구문에서 포인터를 해제해줘야겠지만 스마트 포인터는 객체가 제거되면서 메모리 해제가 실행이 된다고 한다.
메모리 관리 전략은 크게 두가지가 있다고 한다.
하나의 메모리를 접근하는 포인터는 무조건 하나만 존재하게 만드는 방식
해당 메모리를 바라보는 포인터가 증가하거나 감소할 때 카운트를 관리하는 방식 - 카운트가 0이 되면 메모리를 해제
1. unique_ptr
유니크 포인터는 첫 번째 고유 소유권 방식의 포인터이다.
유니크 포인터를 생성하면 다른 유니크 포인터를 선언하고 대입하는 것은 불가능하다.
언급했다시피 해당 메모리는 하나의 포인터만 접근할 수 있다. 만약 다른 포인터가 그 메모리에 접근하고 싶다면 move 연산자를 사용해야한다.
물론 move한 이후 첫 번째 포인터는 비어있을 것이다.
auto ptr1 = std::make_unique<int>(1);
cout << *ptr1 << endl;
unique_ptr<int> ptr2 = move(ptr1);
if (!ptr1)
{
std::cout << "ptr1 is empty" << std::endl;
}
따라서 이 코드를 실행하면 ptr1 is empty가 출력된다.
2. shared_ptr
메모리를 바라보는 포인터들을 카운트해서 관리하는 방식이다.
따라서 shared_ptr 객체에 use_count라는 reference count 변수가 존재한다.
auto ptr3 = std::make_shared<int>(11);
cout << ptr3.use_count() << endl;
shared_ptr<int> ptr4 = ptr3;
cout << ptr3.use_count() << endl;
ptr4.reset();
cout << ptr3.use_count() << endl;
출력 결과는 다음과 같다.
1
2
1
굉장히 편리해보이지만 shared_ptr은 큰 단점이 하나 있는데 두 개의 다른 shared 포인터가 서로를 가리키고 있다면 카운트가 0이 될 수 없는 순환 참조가 발생하게 된다.
3. weak_ptr
weak_ptr은 shared_ptr의 단점인 순환 참조를 방지하기 위한 포인터로 shared_ptr를 관리하는 카운트에 포함되지 않는다.
따라서 위 코드에서 weak_ptr로 ptr3을 대입해도 reference count는 증가하지 않는다. 즉 shared_ptr의 객체의 수명에 관여하지 않는 포인터이다.(디버거로 살펴보면 strong/weak refCount가 따로 표시된다고한다.)
개념은 충분히 이해가 가는데 나중에 순환 참조 관련된 예제와 weak_ptr을 사용해서 해결하는 코드까지 정리를 한번 해야겠다.
'Language' 카테고리의 다른 글
| [C++] std::generate, std::for_each (0) | 2022.05.08 |
|---|---|
| [C++] stringstream - 문자열 처리 (0) | 2022.04.04 |