ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • C++ 스마트 포인터(Smart Pointer) 이해하기
    c++ 2025. 3. 2. 21:34
    728x90

    C++의 스마트 포인터는 메모리 관리 자동화를 위한 도구로, 수동으로 new 및 delete를 호출하는 번거로움을 줄이고 메모리 누수(memory leak) 문제를 방지하는 데 도움을 준다. 스마트 포인터는 C++11 이후부터 표준 라이브러리에 포함되었으며, std::unique_ptr, std::shared_ptr, std::weak_ptr가 주요 타입이다.

    이번 글에서는 스마트 포인터의 종류와 사용법을 예제와 함께 자세히 살펴본다.


    1. std::unique_ptr - 단일 소유권 스마트 포인터

    특징

    • 객체에 대한 단 하나의 소유자만 존재할 수 있다.
    • 복사가 불가능하지만 이동(move) 가능하다.
    • 객체가 소유권을 잃으면 자동으로 해제된다.

    사용 예제

    #include <iostream>
    #include <memory>
    
    class Resource {
    public:
        Resource() { std::cout << "Resource 생성" << std::endl; }
        ~Resource() { std::cout << "Resource 소멸" << std::endl; }
    };
    
    int main() {
        std::unique_ptr<Resource> ptr1 = std::make_unique<Resource>();
        // std::unique_ptr<Resource> ptr2 = ptr1; // 오류: 복사 불가
    
        std::unique_ptr<Resource> ptr2 = std::move(ptr1); // 이동 가능
        if (!ptr1) {
            std::cout << "ptr1은 더 이상 리소스를 소유하지 않음" << std::endl;
        }
    }
    

    주요 메서드

    메서드 설명

    std::make_unique<T>(args...) unique_ptr 객체 생성 (C++14 이상)
    std::move(ptr) 소유권 이전(이동)
    release() 소유권 해제 후 원시 포인터 반환
    reset() 새로운 객체를 가리키도록 변경

    2. std::shared_ptr - 참조 카운트 기반 스마트 포인터

    특징

    • 여러 개의 shared_ptr가 하나의 객체를 공유할 수 있다.
    • 참조 카운트(reference count)를 유지하여 마지막 shared_ptr가 소멸될 때만 객체가 해제된다.
    • 복사 및 이동이 가능하다.

    사용 예제

    #include <iostream>
    #include <memory>
    
    class Resource {
    public:
        Resource() { std::cout << "Resource 생성" << std::endl; }
        ~Resource() { std::cout << "Resource 소멸" << std::endl; }
    };
    
    void useResource(std::shared_ptr<Resource> ptr) {
        std::cout << "useResource 내부의 참조 카운트: " << ptr.use_count() << std::endl;
    }
    
    int main() {
        std::shared_ptr<Resource> ptr1 = std::make_shared<Resource>();
        std::cout << "초기 참조 카운트: " << ptr1.use_count() << std::endl;
        
        std::shared_ptr<Resource> ptr2 = ptr1; // 공유됨
        std::cout << "ptr2 생성 후 참조 카운트: " << ptr1.use_count() << std::endl;
        
        useResource(ptr1);
        
        ptr2.reset();
        std::cout << "ptr2 해제 후 참조 카운트: " << ptr1.use_count() << std::endl;
    }
    

    주요 메서드

    메서드 설명

    std::make_shared<T>(args...) shared_ptr 객체 생성
    use_count() 현재 참조 카운트 반환
    reset() 소유권 해제

    3. std::weak_ptr - 순환 참조 방지용 스마트 포인터

    특징

    • shared_ptr와 함께 사용되며 참조 카운트를 증가시키지 않는다.
    • shared_ptr가 관리하는 객체가 해제되었는지 확인할 수 있다.
    • lock()을 사용하여 shared_ptr로 변환 후 객체를 안전하게 사용한다.

    사용 예제

    #include <iostream>
    #include <memory>
    
    class Resource {
    public:
        Resource() { std::cout << "Resource 생성" << std::endl; }
        ~Resource() { std::cout << "Resource 소멸" << std::endl; }
    };
    
    int main() {
        std::shared_ptr<Resource> sharedPtr = std::make_shared<Resource>();
        std::weak_ptr<Resource> weakPtr = sharedPtr; // 참조 카운트 증가 없음
        
        if (auto locked = weakPtr.lock()) {
            std::cout << "객체가 아직 존재함" << std::endl;
        }
        
        sharedPtr.reset(); // 객체 해제
        
        if (weakPtr.expired()) {
            std::cout << "객체가 삭제됨" << std::endl;
        }
    }
    

    주요 메서드

    메서드 설명

    lock() shared_ptr로 변환 (객체가 존재하면)
    expired() 객체가 해제되었는지 확인

    4. 스마트 포인터 사용 시 주의할 점

    1. 순환 참조 방지
      • shared_ptr를 사용할 때 객체 간 상호 참조(예: A가 B를 shared_ptr로 가리키고, B도 A를 shared_ptr로 가리키는 경우)가 발생하면 참조 카운트가 감소하지 않아 메모리 누수가 발생할 수 있다.
      • 이를 방지하려면 한쪽은 weak_ptr를 사용해야 한다.
      class B;
      class A {
      public:
          std::shared_ptr<B> b;
      };
      
      class B {
      public:
          std::weak_ptr<A> a; // 순환 참조 방지
      };
      
    2. unique_ptr을 복사하지 않도록 주의
      • unique_ptr는 복사할 수 없고, 이동만 가능하므로 함수 인자로 받을 때는 std::move()를 사용해야 한다.
      void process(std::unique_ptr<Resource> ptr) {
          // ptr은 이동된 후 원래 소유자는 더 이상 접근할 수 없음
      }
      
    3. 스마트 포인터를 불필요하게 사용하지 않기
      • 스마트 포인터는 동적 할당된 객체를 관리할 때만 필요하다. 스택 할당된 객체는 스마트 포인터로 감쌀 필요가 없다.

    마무리

    스마트 포인터를 사용하면 C++의 동적 메모리 관리를 보다 안전하고 효율적으로 할 수 있다. unique_ptr, shared_ptr, weak_ptr 각각의 특징과 사용법을 이해하고, 적절한 상황에서 활용하는 것이 중요하다.

    728x90

    'c++' 카테고리의 다른 글

    고급 문법과 최적화  (0) 2025.03.02
    코드 품질과 유지보수성  (0) 2025.03.02
    C++ 구조체 vs 클래스: 차이점과 올바른 활용법  (0) 2025.02.24
    Modern C++  (0) 2025.02.06
    C++ 네트워크 프로그래밍과 소켓 통신  (0) 2025.02.04
Designed by Tistory.