728x90
반응형

C++ 스마트 포인터(std::unique_ptr, std::shared_ptr, std::weak_ptr) 명시적 해제(reset())

  • C++11에서 스마트 포인터(std::unique_ptr, std::shared_ptr, std::weak_ptr)는 동적 메모리 관리를 자동으로 수행하지만, 특정 시점에 명시적으로 내부 리소스를 해제해야 하는 경우가 있습니다.
  • 이를 위해 reset() 메서드를 사용할 수 있습니다. 이 글에서는 각 스마트 포인터의 reset() 동작을 예제 코드와 함께 설명하겠습니다.


1. std::unique_ptrreset()

  • std::unique_ptr단독 소유권 을 갖는 스마트 포인터로, reset()을 호출하면 내부 리소스를 해제하고 nullptr 상태가 됩니다.

  • cpp

      #include <iostream>
      #include <memory>
      
      int main() {
          std::unique_ptr<int> up = std::make_unique<int>(42); // 42를 가리키는 unique_ptr 생성
      
          up.reset();  // 내부 리소스 해제 후 nullptr로 설정
      
          if (up == nullptr) {
              std::cout << "맞아요! nullptr 상태입니다." << std::endl;
          }
      }
    

reset() 동작

  • reset()이 호출되면 내부 객체가 delete 되어 메모리가 해제됩니다.
  • unique_ptr은 소유권을 단독으로 가지므로, 다른 곳에서 해당 객체를 사용할 수 없습니다.


2. std::shared_ptrreset()

  • std::shared_ptr은 여러 개의 shared_ptr이 동일한 객체를 공유할 수 있으며, 참조 카운트(reference count) 를 사용하여 마지막 소유자가 객체를 해제합니다.

  • reset()을 호출하면 해당 shared_ptr만 소유권을 포기하고 참조 카운트가 감소합니다.

  • cpp

      #include <iostream>
      #include <memory>
      
      int main() {
          auto sp1 = std::make_shared<int>(42); // sp1 생성, 참조 카운트: 1
          auto sp2 = sp1;  // sp1는 참조 카운트 증가 1에서 2로 증가. sp2는 sp1과 동일.
      
          std::cout << "sp1.use_count(): " << sp1.use_count() << std::endl;  // 2
          std::cout << "sp2.use_count(): " << sp2.use_count() << std::endl;  // 2
      
          sp1.reset();  // sp1은 nullptr 상태가 되고 참조 카운트 1에서 0으로 감소
                        // sp2는 참조 카운트 2에서 1로 감소
      
          std::cout << "sp2.use_count() after sp1.reset(): " << sp2.use_count() << std::endl;  // 1
      }
    

reset() 동작

  • reset()이 호출되면 해당 shared_ptr이 소유한 객체에 대한 소유권을 포기합니다.
  • 단, 다른 shared_ptr이 같은 객체를 참조 중이면 객체는 삭제되지 않고, 참조 카운트만 감소합니다.
  • 마지막 shared_ptrreset()을 호출하거나 범위를 벗어나면 객체가 삭제됩니다.


3. std::weak_ptrreset()

  • std::weak_ptrstd::shared_ptr이 관리하는 객체를 "약한 참조(weak reference)"로 저장하며, 참조 카운트를 증가시키지 않습니다.

  • reset()을 호출하면 weak_ptr이 참조하는 객체에 대한 연결이 해제됩니다.

  • cpp

      #include <iostream>
      #include <memory>
      
      int main() {
          std::shared_ptr<int> sp = std::make_shared<int>(42); // shared_ptr sp 생성. sp의 참조 카운트 1
          std::weak_ptr<int> wp = sp;  // weak_ptr wp 생성 (참조 카운트 영향 없음)
      
          std::cout << "shared_ptr use_count: " << sp.use_count() << std::endl;  // 1
      
          if (auto locked = wp.lock()) { // weak_ptr를 shared_ptr로 변환하여 사용 가능
              std::cout << "locked value: " << *locked << std::endl;  // 값 42 출력
          } else {
              std::cout << "Object expired" << std::endl;
          }
      
          wp.reset();  // weak_ptr wp의 참조 해제 (weak_ptr의 reset()해도 참조된 shared_ptr sp의 참조 카운트는 영향 없음
          if (auto locked = wp.lock()) { // false 
              std::cout << "locked value: " << *locked << std::endl;
          } else {
              std::cout << "After wp.reset(), object not accessible." << std::endl;
          }
      
          sp.reset();  // shared_ptr sp의 리소스 해제 (객체 삭제)
      
          // weak_ptr wp가 참조하던 객체(sp)가 삭제되었는지 확인
          if (wp.expired()) { // true  
             std::cout << "After sp.reset(), object has expired." << std::endl; 
          } else {
             std::cout << "Object still alive." << std::endl;
          }
      }
    

reset() 동작

  • weak_ptrreset()은 내부 참조를 해제하지만, 객체의 생명주기에 영향을 미치지 않습니다.
  • weak_ptr.lock()을 사용하여 shared_ptr로 변환해야만 객체를 안전하게 접근할 수 있습니다.
  • shared_ptr이 삭제된 후 weak_ptr.lock()을 호출하면 nullptr이 반환됩니다.
  • expired()를 사용하여 객체가 삭제되었는지 확인할 수 있습니다.


4. reset()의 사용 사례

4.1. 특정 시점에 리소스 해제

  • 어떤 특정 이벤트 이후 스마트 포인터의 리소스를 즉시 해제하고 싶다면 reset()을 사용할 수 있습니다.

  • cpp

      std::unique_ptr<int> up = std::make_unique<int>(100);
      up.reset();  // 리소스 즉시 해제
    

4.2. shared_ptr을 사용한 객체 공유 중 특정 소유자 해제

  • 여러 shared_ptr이 같은 객체를 공유할 때, 일부 shared_ptr만 명시적으로 해제할 수 있습니다.

  • cpp

      auto sp1 = std::make_shared<int>(10);
      auto sp2 = sp1;  // 참조 카운트: 2
    
      sp1.reset();  // sp1만 해제, 객체는 유지됨
    

4.3. weak_ptr을 사용한 캐싱

  • weak_ptr을 캐싱에 활용하고 필요할 때만 객체를 생성하도록 할 수 있습니다.

  • cpp

      std::shared_ptr<int> sp = std::make_shared<int>(50);
      std::weak_ptr<int> cache = sp; // 캐싱
      
      sp.reset();  // 객체 삭제
      
      if (cache.expired()) {
          std::cout << "Cache expired, reload required!" << std::endl;
      }
    


5. 요약

  • std::unique_ptr::reset() : 객체를 즉시 삭제하고 nullptr로 설정합니다.
  • std::shared_ptr::reset() : 참조 카운트를 감소시키고, 마지막 shared_ptr이 삭제될 때 객체를 해제합니다.
  • std::weak_ptr::reset() : shared_ptr의 객체와의 연결을 해제하지만, 객체 소멸에는 영향을 주지 않습니다.

  • 스마트 포인터를 사용할 때 reset()을 적절히 활용하면 메모리를 효과적으로 관리할 수 있습니다.
  • 하지만, 과도한 reset() 사용은 불필요한 리소스 해제를 초래할 수 있으므로 주의해야 합니다.



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

728x90
반응형

+ Recent posts