ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [C++] 스마트 포인터 활용하기
    C++/C++ 실습 2025. 6. 1. 11:22

    스마트 포인터란?

    C++에서 스마트 포인터 (Smart Pointer)는 동적 메모리 관리를 자동화해주는 클래스를 의미한다. 기존 new, delete를 통해 동적 메모리 할당을 하던 방식에서 발전된 형태라고 볼 수 있다. 가장 큰 장점으로는 메모리 해제 (delete)를 하지 않더라도 자동적으로 메모리를 해제한다는 점이다.

     

    스마트 포인터 종류

    스마트 포인터 설명
    std::unique_ptr<T> 단독 소유 포인터이며, 다른 포인터로 복사가 불가능하다. 이동은 가능하다.
    std::make_uniqure<T> 함수를 통해 생성할  수 있다. (C++ 14이상부터 가능하며, C++ 11에서는 직접 new 함수를 써야된다.)
    std::shared_ptr<T> 여러 포인터가 같은 자원을 공유한다. 마지막 포인터가 삭제될 때 자원이 해제된다.
    std::make_shared<T> 함수를 통해 생성할 수 있다.
    std::weak_ptr<T> std::shared_ptr과 함께 사용한다. 참조는 하지만 소유하지 않는다. 순환 참조 방지를 위해 사용된다.

     

     

    std::unique_ptr 예시

    #include <iostream>
    #include <memory>
    
    class Dog {
    public:
        Dog() { std::cout << "Dog created\n"; }
        ~Dog() { std::cout << "Dog destroyed\n"; }
        void bark() { std::cout << "Woof!\n"; }
    };
    
    int main() {
        std::unique_ptr<Dog> dog = std::make_unique<Dog>();  // C++14 이상
        dog->bark();
    
        // std::unique_ptr<Dog> dog2 = dog; //복사 불가
        std::unique_ptr<Dog> dog2 = std::move(dog); //이동 가능
    
        if (!dog) std::cout << "dog is nullptr\n";
    }

     

    스마트 포인터는 변수형 뿐만 아니라, 클래스에서도 사용 가능하다. std::uniqure_ptr를 통해 Class Dog를 생성하고, 이를 다시 std::move를 써서 dog2로 이전했다. std::move를 통해 복사가 아닌 이동을 했기 때문에, dog에는 더 이상 리소스가 남지 않게 된다.

    std::uniqure_ptr 예제

     

    std::shared_ptr 예시

    #include <iostream>
    #include <memory>
    
    class Cat {
    public:
        Cat() { std::cout << "Cat created\n"; }
        ~Cat() { std::cout << "Cat destroyed\n"; }
        void meow() { std::cout << "Meow!\n"; }
    };
    
    int main() {
        std::shared_ptr<Cat> cat1 = std::make_shared<Cat>();
        std::shared_ptr<Cat> cat2 = cat1;  // 공유 소유
    
        std::cout << "Use count: " << cat1.use_count() << "\n";  // 2
        cat1->meow();
    
        cat1.reset();  // cat1이 소유권 포기, cat2만 소유
        std::cout << "Use count after reset: " << cat2.use_count() << "\n";  // 1
    }

     

    std::shared_ptr은 공동 소유가 가능하다.

    std::shared_ptr<Cat> cat2 = cat1 : 해당 구문을 통해 cat1, cat2는 동일 리소스를 참조할 수 있게 되었다. 

    use_count() : 해당 shared_ptr의 리소스가 몇 군데에서 공유중인지 확인 가능하다.  

    reset() : std::shared_ptr의 소유권을 포기하게 된다. 

     

    std::shared_ptr 예제 결과

    std::weak_ptr 예시

    #include <iostream>
    #include <memory>
    
    class B;  // 전방 선언
    
    class A {
    public:
        std::shared_ptr<B> b_ptr;
        ~A() { std::cout << "A destroyed\n"; }
    };
    
    class B {
    public:
        std::weak_ptr<A> a_ptr;  // 순환 참조 방지 위해 weak_ptr 사용
        ~B() { std::cout << "B destroyed\n"; }
    };
    
    int main() {
        std::shared_ptr<A> a = std::make_shared<A>();
        std::shared_ptr<B> b = std::make_shared<B>();
    
        a->b_ptr = b;
        b->a_ptr = a;  // weak_ptr 사용으로 A, B 모두 정상적으로 파괴됨
    }

     

    std::weak_ptr은 std::shared_ptr과 달리, 소유하지 않고 참조만 한다. 만약 둘다 std::shared_ptr를 쓸 경우, a는 b를 소유하고, b는 a를 소유하게 되면서, 순환 참조 및 메모리 누수가 발생하게 된다. 이를 해결하기 위해, 둘 중에 한 개만 weak_ptr를 적용하게 된다면, 순환 참조 및 메모리 누수를 막을 수 있다.

     

    weak_ptr 예시

     

     a가 소멸되면서, 자동적으로 b도 소멸된다. 따라서, 이후 b는 추가적으로 소멸되지 않는다. 만약 B에 weak_ptr 대신 shared_ptr를 쓰게된다면, a, b 모두 소멸되지 않는 현상이 발생한다.

     

    개념 참조 소유
    의미 객체를 가리키기만 함 객체의 생명주기를 책임짐
    책임 객체가 죽으면 따라감 객체가 살아있게 만드는 역할
    생명 주기 다른 소유자에게 의존 내가 삭제되면, 같이 삭제됨
    스마트 포인터 weak_ptr shared_ptr, unique_ptr
Designed by Tistory.