프로그래밍C++ 중급 개발자

C++에서 일반 포인터와 스마트 포인터(예: std::unique_ptr) 사이의 차이점을 설명하십시오. 스마트 포인터가 프로그램을 더 안전하게 만드는 이유는 무엇입니까?

Hintsage AI 어시스턴트로 면접 통과

답변.

질문 배경:

일반(원시) 포인터는 C++에서 동적 메모리 작업을 위한 전통적인 메커니즘입니다. 이 기법은 범용적이며 추상적이지만 불행히도 메모리 누수, 이중 삭제, 댕글링 포인터 오류와 같은 여러 오류에 매우 취약합니다. 그래서 C++11부터는 표준 라이브러리에 메모리 수명 관리를 자동으로 수행하는 스마트 포인터 템플릿 클래스(std::unique_ptr, std::shared_ptr, std::weak_ptr)가 포함되었습니다.

문제:

일반 포인터를 사용할 때 메모리 할당 및 해제에 대한 책임이 프로그래머에게 있습니다. 메모리 해제에서의 오류는 메모리 누수, 데이터 손상 및 프로그램 충돌로 이어질 수 있습니다. 예외 처리 또는 다른 함수에 포인터를 전달할 때 복잡한 경우가 특히 발생합니다.

해결책:

스마트 포인터는 할당된 메모리를 캡슐화하고 더 이상 소유자가 없을 때 자동으로 해제합니다. 이들은 RAII를 구현합니다. std::unique_ptr는 독점 소유권을 제공하고, std::shared_ptr는 공유 소유권을 제공하며, std::weak_ptr는 "참조 순환"을 방지하기 위해 제어되지 않는 소유권을 제공합니다.

코드 예:

#include <memory> void foo() { std::unique_ptr<int> p = std::make_unique<int>(5); // 예외 발생 시에도 메모리가 해제됩니다 // ... }

주요 특징:

  • 스마트 포인터는 자동으로 리소스를 해제합니다
  • std::unique_ptr는 복사를 금지하며, 오직 이동 의미론(moving semantics)만 있습니다
  • std::shared_ptr는 "참조 횟수"를 구현합니다

함정 질문.

std::unique_ptr를 배열에 사용 할 수 있습니까? (new[]로 생성된 배열에서)

아니요, 배열의 경우 std::unique_ptr<T[]>를 사용해야 합니다: 이 경우 delete[]가 호출됩니다.

std::unique_ptr<int[]> arr(new int[10]);

표준 스마트 포인터가 모든 메모리 누수를 방지할 수 있습니까?

아니요. 예를 들어, std::shared_ptr 간의 순환 참조는 메모리 누수로 이어질 수 있습니다. 이러한 순환을 끊기 위해 std::weak_ptr를 사용합니다.

스마트 포인터가 동일한 객체를 두 번 "삭제"할 수 있습니까?

아니요, 소유권 논리를 위반하지 않는 경우(원시 포인터를 사용하지 않는 경우) 그렇지 않습니다! 원시 포인터를 수동으로 복사하면 두 번 삭제가 발생할 수 있습니다.

전형적인 오류 및 안티 패턴

  • std::unique_ptr를 복사하면 컴파일 오류가 발생합니다
  • 동일한 객체에 대해 스마트 포인터와 원시 포인터를 동시에 사용하십시오
  • 순환 의존성을 해결하기 위해 std::weak_ptr를 사용하지 마십시오

사례 연구

부정적인 사례

원시 포인터가 사용되고 수동으로 메모리가 해제되며, 예외 발생 시 메모리가 해제되지 않습니다.

장점:

  • 간단한 문제에 대해 명확합니다

단점:

  • 정기적인 메모리 누수
  • 잠재적인 이중 삭제
  • 코드 유지 관리가 어렵습니다

긍정적인 사례

std::unique_ptr 및 std::make_unique를 사용하여 객체를 생성하고 함수를 통해 전달합니다.

장점:

  • 메모리 누수가 거의 불가능합니다
  • RAII 인터페이스, 객체 소유 및 전달이 간편합니다

단점:

  • 이동 의미론 및 표준 라이브러리 작업을 이해해야 합니다