728x90
반응형

std::unique_ptr 생성 방법과 예외 안전성

  • std::unique_ptrC++에서 단일 소유권을 보장하는 스마트 포인터(smart pointer)로, 자원 관리와 메모리 해제를 자동으로 처리하여 메모리 누수를 방지하는 데 큰 역할을 합니다.
  • 다만, 생성 방법과 사용 방법에 따라 예외 상황에서 예상치 못한 문제가 발생할 수 있으므로 주의가 필요합니다.


1. make_unique 사용 (가장 권장)

  • cpp

      auto myCellp = std::make_unique<SpreadsheetCell>();
    

  • make_uniqueC++14부터 제공되며,

    • 코드가 간결하고 가독성이 좋습니다.
    • 예외 발생 시에도 자원 누수 위험이 없습니다.
    • 메모리 할당과 객체 생성이 원자적으로 처리되므로 안전합니다.
  • unique_ptr를 생성할 때는 항상 make_unique를 사용하는 것이 표준적으로 권장됩니다.



2. new 연산자 직접 사용 (지양 권장)

  • cpp

      std::unique_ptr<SpreadsheetCell> myCellp(new SpreadsheetCell());
    

  • ⚠️ 이 방법도 가능하지만,
    • 예외 안전성이 부족합니다.
    • 여러 객체를 순차적으로 생성할 때 중간에 예외가 발생하면 이미 생성된 자원이 누수될 수 있습니다.

2.1. 예외 발생 및 자원 누수 사례:

  • cpp

      #include <iostream>
      #include <memory>
      #include <stdexcept>
      
      class SpreadsheetCell {
      public:
          SpreadsheetCell() { std::cout << "SpreadsheetCell 생성\n"; }
          ~SpreadsheetCell() { std::cout << "SpreadsheetCell 소멸\n"; }
      };
      
      class ThrowingConstructor {
      public:
          ThrowingConstructor() { 
              std::cout << "ThrowingConstructor 생성\n"; 
              throw std::runtime_error("생성 중 예외 발생!");
          }
      };
      
      int main() {
          try {
              // SpreadsheetCell 객체가 먼저 생성되지만
              // 다음 줄에서 예외가 발생하면 이미 생성된 자원은 해제되지 않고 누수 발생
              std::unique_ptr<SpreadsheetCell> ptr1(new SpreadsheetCell());
              ThrowingConstructor obj; // 예외 발생
      
              // ptr1이 스택에 완전히 초기화되기 전에 예외가 발생하면서 소멸자가 호출되지 않음
          }
          catch (const std::exception& e) {
              std::cout << "예외 잡음: " << e.what() << "\n";
          }
      
          return 0;
      }
    

  • 👉 이런 문제를 피하려면 make_unique를 사용하는 것이 좋습니다.


2.2. 이미 생성된 raw pointerunique_ptr로 감싸는 경우

  • cpp

      SpreadsheetCell* rawPtr = new SpreadsheetCell();
      std::unique_ptr<SpreadsheetCell> myCellp(rawPtr);
    

  • ⚠️ 이 방법은 가능하지만 위험합니다.
    • rawPtr이 다른 코드에서 소유권을 중복해서 참조하고 있을 경우 문제가 발생합니다.
    • 같은 포인터를 두 번 delete하는 잘못된 동작이나, 소유권 혼동으로 인한 버그로 이어질 수 있습니다.
    • 또한 unique_ptr 생성 직후 raw pointer를 다시 사용하려는 경우 프로그램 오류나 undefined behavior를 초래합니다.

  • 👉 따라서 raw pointerunique_ptr로 감싸는 방법은
    • 소유권 이전의 의도가 명확하고, 다른 곳에서 해당 포인터를 절대 참조하지 않을 때만 사용 해야 하며,
    • 일반적으로는 권장하지 않습니다.


3. 정리

  • 방법 장점 단점 및 주의사항
    std::make_unique 사용 안전함, 간결함, 예외 발생 시에도 누수 없음 가장 추천되는 표준적 방법
    new 연산자 직접 사용 코드상에서 메모리 할당이 명확히 보임 예외 발생 시 자원 누수 가능성이 있으며, 특별한 상황이 아니면 지양 권장
    이미 생성된 raw pointer 감싸기 기존 raw pointer를 이전할 때 사용할 수 있음 소유권 혼동 및 중복 해제 위험이 있으며, 꼭 필요한 상황 외에는 권장하지 않음

  • 📌 결론적으로 std::make_unique를 사용하는 것이 가장 안전하며,
  • 특수한 상황(생성자가 protected이거나, 복잡한 초기화 과정이 필요한 경우 등)을 제외하고는 new 연산자 직접 사용과 raw pointer 감싸기는 피하는 것이 좋습니다.



  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형

+ Recent posts