Outdated/Library

[Modern C++ 14] Smart Pointer - std::shared_ptr

해달 2018. 1. 2. 22:08

Modern C++

주제 : Smart Pointer(std::unique_ptr, std::shared_ptr, std::weak_ptr)
헤더 : <memory>
설명
모던 C++에는 스마트 포인터 객체가 추가되었다. 기존 C 스타일의 포인터는 동적으로 생성된 객체들을 프로그래머가 잘 제어했어야 했다. 메모리가 잘 관리되지 않는다면 메모리 누수(memory leak), 댕글링 포인터(dangling pointer), 중복 삭제(double free) 등의 문제가 생길 수 있다. 스마트 포인터는 RAII(Resource Acquisition Is Initialization) 관용구를 이용해 메모리 해제를 자동화시킴으로써 프로그래머의 실수를 줄여 보다 더 안전한 프로그래밍을 할 수 있게 되었다. 또한, 템플릿으로 작성되어, 어떤 타입이든 사용 가능하다. 스마트 포인터는 세 가지로 나뉘는 데, 이번 게시물에는 std::shared_ptr에 대해서 알아보도록 하겠다.

- std::shared_ptr

std::shared_ptr는 참조 횟수를 이용해 자원을 관리하는 방식이다. 이름에서 알 수 있듯, 한 객체에 관해 여러 포인터가 가리킬 수 있으며(소유할 수 있으며), 가리키는 포인터의 갯수만큼 참조 횟수가 올라간다. 참조 횟수가 0이 되면 객체는 자동으로 소멸된다. 사용 방법은 다음과 같다.

#include <memory>
#include <iostream>
using namespace std;

int main()
{
    // 초기화 방법
    shared_ptr<int> p1(new int(10));
    shared_ptr<int> p2(std::make_shared<int>(10));
    shared_ptr<int> p3 = std::make_shared<int>(10);
    {
        // 참조 횟수 1 증가. 현재 2
        shared_ptr<int> p4 = p1;
        cout << "현재 참조 횟수 : " << p1.use_count() << endl;
    }
    // 참조 횟수 1 감소. 현재 1
    cout << "현재 참조 횟수 : " << p1.use_count() << endl;

    // 기존 포인터처럼 사용 가능
    // *, -> 오버로딩이 되어있다.
    cout << *p1 << "(" << (void*)p1.get() << ")" << endl;
}

초기화는 크게 두 가지로 볼 수 있다. 하나는 C 스타일의 포인터를 받는 방법과 라이브러리에서 제공하는 std::make_shared를 이용하는 것이다. 후자는 전자보다 최적화되어 있으므로, 가급적 후자를 권장한다. 앞서 말했듯, 참조 횟수를 이용해서 객체를 관리하게 되는데, 이를 위해 '제어 블록(control block)'이란 것을 이용하게 된다. 쉽게 말하면 이걸 관리하는 매니저 객체가 있다고 생각하면 된다. 그림으로 나타내면 다음과 같다.

MSDN std::shared_ptr

사진 출처 : MSDN

생성자의 두 번째 매개변수로 삭제자(deleter)를 지정해 사용자 정의 삭제자로 객체를 삭제할 수 있다. 이를 응용해 이중으로 해제되는 상황도 방지할 수 있다.

주로 사용되는 메소드는 다음과 같다.

이름설명
get자원에 대한 포인터를 반환한다.
use_count참조 횟수를 반환한다.
reset자원을 재설정 한다.
swap인자로 전달된 포인터와 자원을 치환한다.

ps 1

C++ 런타임은 참조 횟수에 대한 호출이 원자적 연산(atomic operation)임을 보장한다. 따라서, C 스타일 포인터나 std::unique_ptr보다 시간과 메모리를 더 사용한다.

ps 2

raw 포인터와 shared_ptr를 같은 객체를 가리키게 해서 혼용하는 것은 바람직하지 않다.

Terminology
메모리 누수
동적으로 객체 생성 및 사용 후, 메모리를 시스템에 반환하지 않아 누적되면 메모리가 부족해지는 현상
댕글링 포인터
포인터 변수가 가리키는 객체를 이미 삭제해서 다른 용도로 메모리를 사용중임에도 이 공간을 그 포인터 변수로 다시 참조하는 경우
중복 삭제
한 코드에서 객체를 삭제했는 데, 다른 코드에서 그 메모리를 다시 삭제하는 경우


'Outdated > Library' 카테고리의 다른 글

[Modern C++] Smart Pointer - std::unique_ptr  (0) 2018.01.09
[Modern C++ 14] Smart Pointer - std::weak_ptr  (0) 2018.01.04
[Modern C++]Initializer_list  (0) 2017.11.24