ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • shared_ptr 사용할 때 주의 사항
    개발/C·C++ 2022. 1. 9. 14:22

    cpprerefence 문서를 보면 구현에 관한 가이드 중에서 다음과 같은 부분이 있습니다.

    The destructor of shared_ptr decrements the number of shared owners of the control block. If that counter reaches zero, the control block calls the destructor of the managed object. The control block does not deallocate itself until the std::weak_ptr counter reaches zero as well.

    shapred_ptr의 소멸자는 컨트롤 블록을 공유하는 소유자 수를 감소시킵니다. 만약 카운터가 0에 도달하면 컨트롤 블록은 자신(shared_ptr 객체)의 소멸사를 호출합니다. 컨트롤 블록은 std::weak_ptr 카운터가 0이 될 때까지 메모리를 해지하지 않습니다.

    shared_ptr 객체가 소멸자를 호출할 수 없으면 공유 개수를 올바르게 관리할 수 없다는 의미입니다. shared_ptr 객체의 소멸자가 제대로 호출되려면 shared_ptr 객체를 들고 있는 객체의 소멸자가 정상적으로 호출되어야 합니다. C++에서는 new 연산자를 이용해 메모리를 할당할 때 operator new 연산자로 메모리를 얻고 생성자를 통해 객체를 초기화하는 과정을 거치기 때문에, 메모리를 해제 과정에서는 생성 순서와 반대로, 소멸자를 통해 객체를 정리하고 operator delete 연산자를 통해 메모리를 해제합니다.

     

    특정 객체의 소멸자가 호출되지 않는 상황에는 어떤 것이 있을까요? 여러가지를 생각해 볼 수 있겠지만, 오류를 일으키지도 않고 의도에 따라서는 붙이지 않아도 되는 소멸자의 virtual 키워드입니다. 즉, 부모 클래스의 소멸자가 가상함수가 아닐 때, 자식 클래스의 소멸자는 호출되지 않습니다. 그리고 그 자식 클래스에서 자신이 관리하는 shared_ptr 객체가 존재한다면 문제가 생깁니다. 예제를 통해 알아보겠습니다.

    #include <iostream>
    #include <memory>
    using std::cout;
    using std::endl;
    
    class Test
    {
    public:
    	Test()
    	{
    		cout << "Test" << endl;
    	}
    
    	~Test()
    	{
    		cout << "~Test" << endl;
    	}
    };
    
    class P
    {
    public:
    	P()
    	{
    		cout << "P" << endl;
    	}
    
    	~P()
    	{
    		cout << "~P" << endl;
    	}
    };
    
    class C : public P
    {
    public:
    	C()
    	{
    		cout << "C" << endl;
    	}
    
    	~C()
    	{
    		cout << "~C" << endl;
    	}
    
    private:
    	std::shared_ptr<Test> test{ std::make_shared<Test>() };
    };
    
    int main()
    {
    	P* p = new C();
    	delete p;	
    }

    shared_ptr를 사용하는 목적에 맞게 동작하는 것을 의도했다면 ~Test, ~C가 출력되어야 합니다. 위에 언급한 가이드를 다시 한 번 보겠습니다. 소멸자가 호출되어야 메모리를 정리한다고 했습니다. 이 상황에서는 test라는 shared_ptr 변수는 스택에서 제거되겠지만, 내부적으로 들고 있는 컨트롤 블록과 Test 포인터는 사라지지 않습니다. 메모리 누수가 일어납니다. 이 때에는 반드시 부모 클래스의 소멸자에 virtual 키워드를 붙여줘야 정상적으로 메모리가 해제됩니다.

    '개발 > C·C++' 카테고리의 다른 글

    문자열 파싱 연습하기(feat. strtok_s)  (0) 2024.07.04
    전역변수의 반환  (0) 2022.01.09
    전달 참조(보편 참조)  (0) 2022.01.08
    RVO, NRVO  (0) 2022.01.08
    constexpr  (0) 2022.01.06

    댓글

Designed by Tistory.