본문 바로가기
프로그래밍/C++

C++ std::shared_ptr 댕글링 포인터 발생 상황

by bantomak 2026. 6. 3.
반응형

std::shared_ptr 사용 시 주의사항

C++에서 제공하는 스마트 포인터인 std::shared_ptr을 사용하면 마치 지역변수를 사용하듯이 편하게 포인터를 사용하는 것이 가능하다. 하지만 편한 만큼 잘못 사용하면 찾아내기 힘든 버그를 만들 수 있기 때문에 잘 알고 사용해야 한다.

 

아래에서 std::shared_ptr 사용 시에 발생할 수 있는 문제 상황에 대해서 알아보자.

문제 발생 코드

class MyClass
{
public:
	MyClass() = default;
	~MyClass() = default;

	int a;
	int b;

	void Func()
	{
		std::cout << a << std::endl;
	}
};

int main() 
{
	std::unordered_map<int32_t, std::shared_ptr<MyClass>> myContainer;

	// original은 고유하게 유지되어야하기 때문에 복사된다. 참조 횟수 증가
	myContainer.emplace(1, std::make_shared<MyClass>());

	// 지역 변수로 const auto& 참조 받기
	// 중요: 이때 복사가 일어나지 않으므로 참조 횟수는 여전히 1이다.
	const auto& iter = myContainer.find(1);
	const auto& refClass = iter->second;

	std::cout << "[2] 참조 변수 대입 후 참조 횟수: " << refClass.use_count() << std::endl;
	std::cout << "refClass 주소: " << &refClass << std::endl;

	refClass->Func();

	// 원본을 erase하여 메모리 해제
	// iter가 가리키던 MyClass 객체는 이 순간 소멸
	myContainer.erase(iter);

	// 댕글링 포인터 발생!
	// refClass는 이미 메모리에서 해제된 객체의 주소를 가리키고 있다.
	// 프로그램이 크래시(Crash)가 나거나, 메모리 오염으로 인해 쓰레기 값이 출력될 수 있음.
	std::cout << "[4] 댕글링 포인터로 접근 시도:\n";

	// null 체크로는 댕글링 포인터가 발생했는지 체크할 수 없음
	if (refClass != nullptr)
	{
		refClass->Func(); // 미정의 동작 (위험!)
	}

	return 0;
}

문제 발생 이유

문제가 발생한 이유는 무엇인가?

  • std::shared_ptr을 참조로 refClass 지역변수 선언
  • 참조로 사용하던 와중에 원본이 제거되면서 참조가 가리키던 std::shared_ptr이 이미 사라진 상황
  • 해당 상황에서 참조로 접근하면 미정의 동작이 발생(!)
  • 실무 환경에서는 std::shared_ptr로 선언되어 있기 때문에 문제가 간헐적으로 발생할 수 있어서 더욱 찾기가 어렵다.

해결 방법

  • 차라리 std::shared_ptr을 참조가 아닌 복사하여 사용한다.
  • 복사해서 사용하면 해당 스코프에서는 유효하기 때문에 안전하게 사용이 가능하다.

댓글