C++에서는 메모리 관리를 위해 명시적인 연산자 new와 delete를 사용하여 동적으로 메모리를 할당하고 해제합니다. 그러나 수동 관리에서는 메모리 누수나 이중 해제를 쉽게 발생시킬 수 있습니다.
C++11 표준의 도입으로 std::unique_ptr, std::shared_ptr, std::weak_ptr와 같은 스마트 포인터가 도입되었습니다. 이들은 객체의 생명 주기를 자동으로 관리하며, 예외 발생 시에도 범위를 벗어날 때 자원을 해제함을 보장합니다.
비교 예시:
// 수동 메모리 해제 Foo* ptr = new Foo(); // ... delete ptr; // 스마트 포인터를 사용하여 더 안전하게 std::unique_ptr<Foo> ptr2 = std::make_unique<Foo>(); // delete가 필요 없음 — 모든 작업은 unique_ptr가 처리합니다.
스마트 포인터는 메모리 누수의 위험을 줄이고 코드를 더 안전하고 읽기 쉽게 만듭니다.
같은 포인터에 대해 delete를 두 번 호출하면 어떤 일이 발생하나요?
답변: 첫 번째 delete 호출 후 메모리가 이미 해제됩니다. 다시 delete를 호출하면 정의되지 않은 동작(undefined behavior)이 발생합니다. 예시:
Foo* p = new Foo(); delete p; delete p; // 오류!
이런 상황을 피하기 위해 해제 후 포인터를 nullptr로 설정하십시오:
delete p; p = nullptr;
이야기
대규모 C++ 프로젝트에서 개발자가
delete를 통해 수동으로 동적 메모리를 해제했으나, 예외가 발생한 함수의 여러 분기에서 포인터를 삭제하는 것을 잊었습니다. 이로 인해 부하 테스트 중에 발견된 메모리 누수가 발생했습니다.
이야기
서로 다른 코드 부분 간에 원시 포인터를 공유하려 할 때 메모리의 이중 해제가 발생했습니다 — 일부 코드 부분이
delete를 수행하였으나 나머지 부분에 알리지 않았습니다. 결과: 운영 중에 segfault 발생, core dump 분석에서 즉시 원인을 확인했습니다.
이야기
오래된 프로젝트를 새로운 C++ 표준으로 마이그레이션한 후 원시 포인터가 포함된 코드 일부를 남겨두고 새로운 클래스는 이미 스마트 포인터를 사용하고 있었습니다. 자원 소유권 전달에서 오류가 발생하여 메모리가 '두 번' 해제되는 일이 발생했습니다 — 처음에는 수동으로, 그 다음에는 스마트 포인터 소멸자에 의해 자동으로 해제되었습니다.