엔지니어 동행하기

std::shared_ptr 설명과 예제 본문

Modern C++/Smart Pointer

std::shared_ptr 설명과 예제

엔지니어 설리번 2022. 6. 6. 09:13
반응형
Smart Pointer는 RAII idiom 기법을 따르며, C++에서 메모리 관리를 위한 핵심 개념입니다. 그중 두 번째로 Shared ownership을 제공하는 std::shared_ptr에 대해 설명드리겠습니다. std::shared_ptr는 잘못 사용할 경우, Circular reference가 발생해 Memory leak이 생길 수 있어 주의가 필요합니다. 
또한 std::shared_ptr를 Class의 member variable로 사용할 때, std::unique_ptr과 어떻게 다른지 분석하였습니다.

 

Shared ownership

하나의 Object를 여러 개의 Pointer가 가리킬 수 있다. 그래서 RAII idiom을 따르기 위해 reference counter를 사용한다. 즉, Object를 가리키는 Pointer 개수를 counting 하고, counter가 0이 되면 Destructor를 호출 & 메모리를 해제한다. 

#include <iostream>
#include <memory>

struct D
{
    D() { std::cout << "D::D\n"; }
    ~D() { std::cout << "D::~D\n"; }
    void bar() { std::cout << "D::bar\n"; }
};

int main()
{
    // Create a (shared owned) resource
    std::shared_ptr<D> p = std::make_shared<D>();
    std::cout << p.use_count() << std::endl;  // 1 출력
    
    std::shared_ptr<D> p2 = p; //Compile error 안 남
    std::cout << p.use_count() << std::endl;  // 2출력
}

use_count()는 std::shared_ptr의 member function으로, 해당 shared_ptr가 가리키는 object를 가리키는 shared_ptr (자신을 포함)의 개수를 return 한다. 

Resource life cycle을 개발자가 고민할 필요 없고, object의 ownership을 여러 scope에서 공유할 수 있다.

 

Circular reference 에 의한 Memory leak 

#include <memory>

class D
{
public:
    D(){}
    ~D(){}
public:
    std::shared_ptr<D> member_ptr;
};

int main()
{
    std::shared_ptr<D> p = std::make_shared<D>();
    std::shared_ptr<D> p2 = std::make_shared<D>();
    
    p->member_ptr = p2;
    p2->member_ptr = p;
}

shared_ptr  p 가 가리키는 객체의 member_ptr이 p2가 가리키는 객체를 가리키도록 한다. 

member_ptr이 서로를 가리킴
main stack frame 끝난 후

member_ptr이 서로를 가리키기 때문에, reference count가 1이고, 메모리 해제가 일어나지 않는 상황임을 알 수 있다. 이를 방지히가 위해서는 main 함수 마지막에 다음 코드를 추가해야 한다.

    p->member_ptr = nullptr;
    p2->member_ptr = nullptr;

 

std::shared_ptr  vs  std::unique_ptr //

각 smart pointer가 member variable로 있는 Class를 이해할 수 있어야 한다. 이를 통해 두 pointer를 어떻게 활용하는지, 언제 사용해야 하는지 알 수 있다. 

#include <memory>

class D
{
public:
    D():member_ptr{std::make_unique<int>(0)} {}
    ~D() {}
private:
    std::unique_ptr<int> member_ptr;
}

int main()
{
    D object;
    D object2;
    object = object2;
}

 

shared_ptr에 저장된 값 출력

shared_ptr 또한 포인터이기 때문에 *연산자로 저장된 내용에 접근할 수 있다. 

#include <iostream>
#include <memory> 

int main()
{

    std::shared_ptr<int> sp = std::make_shared<int>(1);
    std::cout << *sp << std::endl; // 1 출력

    return 0;
}

반응형

'Modern C++ > Smart Pointer' 카테고리의 다른 글

std::unique_ptr 설명과 예제  (0) 2022.06.05
Smart Pointer 설명과 예제  (0) 2022.06.05
Comments