Smart Pointer 보충

2024. 12. 24. 21:03·C++

0. Smart Pointer의 메모리 공간

  • 스마트 포인터는 힙 메모리 공간과 스택 메모리공간을 모두 사용한다고 한다.
  • 스마트 포인터 자체는 스택 메모리 공간에 저장된다.
    • 함수 안에서 선언한 스마트 포인터는 함수가 종료되면, 스택에서 사라지고, 메모리도 해제된다
    • 아래의 코드를 실행하면, 생성자와 소멸자 모두 호출된다.
int main()
{
    {
        std::shared_ptr<Entity> sharedEntity = std::make_shared<Entity>();		
    }
}
  • 그러나 스마트 포인터가 관리하는 실제 리소스는 힙 메모리에 저장된다.
    • 아래의 코드처럼 실행하면, std::cin.get(); 이 실행되기 전까지 소멸자가 호출되지 않는다. (좋은 코드는 아닐 것 같음)
    • 이유는 e0가 sharedEntity의 값을 공유했기 때문에, Func() 함수는 끝났지만, 아직 생명 주기는 남아있다.
    • 결과적으로 모든 코드가 종료되면, 그때 소멸자가 호출된다.
    • 결론적으로 reference count 가 0이 되어야만 소멸자가 호출된다.
std::shared_ptr<Entity> e0;

void Func()
{
	std::shared_ptr<Entity> sharedEntity = std::make_shared<Entity>();
	e0 = sharedEntity;
}
int main()
{

	Func();
	std::cin.get();

}

1. shared_ptr

  • 아래 코드를 실행한 결과를 보면 다음과 같다.

  • 위에서 언급한 것과 비슷한 내용으로 sharedEntity는 { } 밖으로 나와 생명주기가 끝났지만, e0 때문에 소멸자가 호출되지 않고 바깥 { } 에서 나와야 소멸자가 호출된다.
#include <iostream>
#include <memory>

class Entity
{
public:
	Entity()
	{
		std::cout << "constructor\n";
	}
	~Entity()
	{
		std::cout << "destructor\n";
	}
	void Print()
	{

	}
};

int main()
{
	{
		std::shared_ptr<Entity> e0;
		{
			std::shared_ptr<Entity> sharedEntity = std::make_shared<Entity>();
			e0 = sharedEntity;
		}
		std::cout << "...\n";
	}

	std::cin.get();
}

2. weak_ptr

  • weak_ptr은 shard_ptr의 값을 공유할 수 있지만, reference count는 늘리지 않는다.
  • 아래의 코드를 실행시키면, e0를 shared_ptr로 만들었을 때의 결과물과 다른 것을 볼 수 있다. 
  • 그 이유는 reference count를 늘리지 않았기 때문에 안쪽 { }를 빠져나오면서 reference count 가 1에서 0이 되고, 소멸자가 호출된 것이다.

int main()
{
	{
		std::weak_ptr<Entity> e0; //weak_ptr !!
		{
			std::shared_ptr<Entity> sharedEntity = std::make_shared<Entity>();
			e0 = sharedEntity;			
		}
		std::cout << "...\n";
	}

	std::cin.get();
}

3. 그렇다면 언제 써야할까

  • 정확한 답은 아니지만, 구글링을 하던 도중 유튜브에서 찾은 내용을 번역해 보았다.
  • 출처:https://www.youtube.com/watch?v=UOB7-B2MfwA

    1. 절대 new와 delete를 사용하지 마세요. 포인터가 객체를 소유하고 있는지 여부는 매우 명확해야 합니다. 만약 포인터가 객체를 소유하고 있다면(예를 들어, 객체를 생성하고 있다면), 스마트 포인터를 사용하여 make_unique 또는 make_shared를 사용하세요.

    2. 기본적으로 오버헤드가 거의 없기 때문에 unique_ptr을 사용하세요. 여러 소유자가 있어야 한다는 것을 알고 있다면 shared_ptr을 사용하세요.

    3. 포인터가 객체를 소유하지 않을 때는 원시 포인터를 사용하세요. 예를 들어, 객체를 함수에 전달할 때 수신한 포인터는 객체를 소유하지 않으므로(함수가 반환되면 객체는 여전히 살아 있을 것입니다) 스마트 포인터를 전달하지 말고 원시 포인터를 전달하세요.
    함수의 원시 포인터로 unique_ptr을 전달하려면, 가장 좋은 방법은 참조를 해제하고 참조로 전달하는 것입니다. 따라서 함수는 예를 들어 "void foo(const Class & myObject)"이며, "foo(*myPointer)"라고 부릅니다.
    다른 방법으로는 "unique_ptr.get()" 메서드를 사용하여 주소 자체를 전달하는 것입니다.

    4. 다시 말해서, 원시 포인터에서 delete를 사용하지 말고 객체를 소유하지 않는다고 가정하세요.

    5. 함수 내부에 객체를 생성하고 반환하려면 unique_ptr로 수행합니다. receiver는 함수를 사용하여 원하는 모든 작업을 수행할 수 있습니다. 이제 함수에서 로컬 객체를 복사하여 전달하면 실제로 복사하는 것이 아니라 r-value로 이동한다는 점을 명심하세요. 따라서 컴파일러 오류 없이 새로운 unique_ptr로 이동합니다. 다시 말하지만, receiver는 원하는 모든 작업을 수행할 수 있으므로 shared_ptr 또는 원시 포인터로 이동할 수 있습니다.

    6. unique_ptr의 소유권을 이전하고 싶을 때는 std::move()를 사용하여 r-value로 할 수 있습니다. 이것이 기본적으로 함수에서 돌아올 때 C++가 하는 일이라고 설명한 것입니다. 함수로 소유권을 이전하는 데 사용할 수 있습니다.

 

  • 아직 명확하게 감이 잡히진 않기에, 나중에 직접 사용하게 되는 경우가 있다면 그 때 느낀 점들을 추가적으로 기록해 두어야 할 거 같다.

'C++' 카테고리의 다른 글

C++ TIL day 9  (3) 2024.12.27
C++ TIL day 8  (0) 2024.12.26
C++ TIL day 7  (2) 2024.12.24
C++ 상속  (0) 2024.12.23
C++ TIL day 6  (0) 2024.12.23
'C++' 카테고리의 다른 글
  • C++ TIL day 9
  • C++ TIL day 8
  • C++ TIL day 7
  • C++ 상속
gbleem
gbleem
gbleem 님의 블로그 입니다.
  • gbleem
    gbleem 님의 블로그
    gbleem
  • 전체
    오늘
    어제
    • 분류 전체보기 (184)
      • Unreal Engine (73)
      • C++ (19)
      • 알고리즘(코딩테스트) (27)
      • TIL (60)
      • CS (4)
      • 툴 (1)
  • 블로그 메뉴

    • 홈
    • 카테고리
  • 링크

    • 과제용 깃허브
    • 깃허브
    • velog
  • 공지사항

  • 인기 글

  • 태그

    싱글턴
    BFS
    actor 클래스
    applydamage
    character animation
    C++
    blend pose
    cin함수
    DP
    템플릿
    const
    map을 vector로 복사
    addonscreendebugmessage
    상속
    매크로 지정자
    gamestate
    enhanced input system
    Vector
    additive animation
    motion matching
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
gbleem
Smart Pointer 보충
상단으로

티스토리툴바