[C++] 스마트 포인터(Smart Pointer) 개념 정리

2025. 6. 12. 20:33·C++

1. 포인터 사용으로 발생할 수 있는 이슈들

포인터는 C++의 강력한 도구이지만, 그와 동시에 치명적인 이슈를 일으키는 원인이기도 하다

포인터 사용으로 발생할 수 있는 이슈들은 대표적으로 다음과 같은 것들이 있다


1) 메모리 누수(Memory Leak)

 메모리를 할당하고 해제하지 않을 경우 힙에 해당 객체가 계속 남게 되는데, 이 경우 해당 코드가 실행될 때마다 힙 메모리를 잡아먹고, 사용하지도 않는 메모리가 계속 할당되어 있는 메모리 누수 문제가 발생한다 보통 c++에서 [new -  delete] 쌍을 맞추면 된다고는 하지만, 코드가 복잡해질수록 포인터끼리 얽히기 쉬워 주해야 한다

2) 널 포인터(nullptr)
아무것도 가리키지 않고 있는 널 포인터를 통해 무언가에 접근할 경우 발생하는 문제이다. 포인터 사용 시 널 포인터 체크를 생활화함으로써 어느 정도 예방할 수 있다

3) 댕글링 포인터(Dangling Pointer)
포인터가 이미 할당이 해제된 메모리를 가리키고 있고, 이를 통해 접근할 경우 발생하는 문제이다. 발생하기도 쉽고 치명적인 이슈이므로 주의해야 한다

 

2. 스마트 포인터

앞서 이야기한 포인터 사용으로 발생할 수 있는 여러가지 문제점을 보완하기 위해 C+11 이상부터 스마트 포인터를 지원하는데, 스마트 포인터는 포인터처럼 사용하는 클래스 템플릿으로 마모리를 자동으로 해제해 준다

C++에 존재하는 스마트 포인터 [ unique_ptr, shared_ptr, weak_ptr ]에 대해 자세히 알아보자

 

3. unique_ptr

unique_ptr은 객체에 대한 단일 소유권을 관리한다 객체의 소유권을 명확히 하고 소유권 이전을 통해 효율적인 자원관리가 가능하다

아래 그림처럼 새로운 소유권을 주장할 때 move를 통해  소유권을 이동하는 식으로 관리된다

단일 소유권 변경

간단한 unique_ptr 사용 예제코드를 확인해 보자

//unique_ptr 사용법
#include <iostream>
#include <memory> // unique_ptr이 들어있는 헤더파일
using namespace std;

int main() {
    // unique_ptr 생성방법
    unique_ptr<int> ptr1 = make_unique<int>(10);

    // unique_ptr이 관리하는 값 출력
    cout << "ptr1의 값: " << *ptr1 << endl;
    //Output: "ptr1의 값: 10"

    // unique_ptr은 복사가 불가능
    // unique_ptr<int> ptr2 = ptr1; // 컴파일 에러 발생!
    
    // 소유권 이동 (move 사용)
    unique_ptr<int> ptr2 = move(ptr1);

    if (!ptr1) {
        cout << "ptr1은 이제 비어 있습니다." << endl;
    }
    cout << "ptr2의 값: " << *ptr2 << endl;
    // 범위를 벗어나면 메모리 자동 해제
    return 0;
}

4. shared_ptr

shared_ptr은 레퍼런스 카운트를 관리한다
레퍼런스 카운트란 현재 객체를 참조하는 포인터의 개수를 카운팅 하는 것이다. 레퍼런스 카운트가 0이 되면 객체는 자동으로 메모리 해제가 된다
이를 활용해서 Dangling Pointer 및 Memory Leak 문제를 효과적으로 방지할 수 있다

아래 그림을 보면 레퍼런스 카운터의 동작을 나타낸 것이다

레퍼런스 카운트 동작

간단한 shared_ptr 사용 예제코드를 확인해 보자

#include <iostream>
#include <memory> // shared_ptr 사용
using namespace std;

int main() {
    // shared_ptr 생성
    shared_ptr<int> ptr1 = make_shared<int>(10);

    // ptr1의 참조 카운트 출력
    cout << "ptr1의 참조 카운트: " << ptr1.use_count() << endl; // 출력: 1

    // ptr2가 ptr1과 리소스를 공유
    shared_ptr<int> ptr2 = ptr1;
    cout << "ptr2 생성 후 참조 카운트: " << ptr1.use_count() << endl; // 출력: 2

    // ptr2가 범위를 벗어나면 참조 카운트 감소
    ptr2.reset();
    cout << "ptr2 해제 후 참조 카운트: " << ptr1.use_count() << endl; // 출력: 1

    // 범위를 벗어나면 ptr1도 자동 해제
    return 0;
}

5. weak_ptr

weak_ptr은 객체의 소유권을 공유하지 않는다
다른 스마트 포인터와 다르게 레퍼런스 카운트를 증가시키지 않는 약한 참조를 한다

shared_ptr은 유용하지만 순환참조가 발생할 수 있다

순환 참조란, 두 개 이상의 객체가 서로를 shared_ptr로 가리켜 참조하는 상황을 말한다. 


이러한 순환 참조는 메모리 누수를 유발할 수 있다

이 상황에서 서로 순환하고 있는 shared_ptr 중 하나를 weak_ptr로 대체하면 순환 고리가 끊어지게 되므로 문제를 해결할 수 있다

정리하면 shared_ptr은 관찰과 소유를 하는 반면, weak_ptr은 관찰만 한다고 표현한다

weak_ptr은 레퍼런스 카운트의 변화가 없음

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

[C++] System() 함수 - "cls" , "pause"  (0) 2025.06.13
[C++] 얕은 복사 vs 깊은 복사  (1) 2025.06.12
[C++] 헤더파일 중복방지 #Pragma once vs #ifndef ~ endif  (0) 2025.06.10
[C++] 깔끔한 코드 작성법 - 헤더파일과 소스파일을 나누는 이유  (0) 2025.06.05
[C++] String 클래스 정리 및 함수 사용  (0) 2025.06.04
'C++' 카테고리의 다른 글
  • [C++] System() 함수 - "cls" , "pause"
  • [C++] 얕은 복사 vs 깊은 복사
  • [C++] 헤더파일 중복방지 #Pragma once vs #ifndef ~ endif
  • [C++] 깔끔한 코드 작성법 - 헤더파일과 소스파일을 나누는 이유
_Bin_
_Bin_
  • _Bin_
    빈코드 (이전중)
    _Bin_
  • 전체
    오늘
    어제
  • 글쓰기 관리
    • 전체보기 (75)
      • C++ (8)
      • Unreal Engine (11)
      • Algorithm (6)
      • 컴퓨터지식(CS) (3)
      • 코딩테스트 (45)
      • 팀프로젝트 (1)
      • 기술면접 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    Algorithm
    AVL Tree
    struct
    내일배움캠프
    SOLID원칙
    프로그래밍
    class
    코딩테스트
    Red-Black Tree
    프로그래머스
    CS
  • 최근 댓글

  • hELLO· Designed By정상우.v4.10.3
_Bin_
[C++] 스마트 포인터(Smart Pointer) 개념 정리
글쓰기상단으로

티스토리툴바