스마트 포인터란?

 

이전에 C# 카테고리로 작성한 garbage collector 부분에서 작성했듯

자바나 C#같은 경우는 garbage collector를 통해 사용자 메모리를 관리하지만,

C++은 사용자가 스스로 메모리를 할당하고 해제하며 관리해주어야 한다. 

 

C언어는 malloc과 free, C++언어는 new와 delete를 통해 메모리 할당 및 해제가 가능하며

이때 할당받은 메모리를 해제하지 않을경우 프로그램은 계속 사용하고 있는 메모리로 인지하게 되며, 이는 메모리 누수의 원인이 된다.

 

이렇듯 메모리 누수를 방지하며 프로그램의 안정성을 보장하기 위한 것이 바로 스마트 포인터라는 기능이다.

스마트 포인터는 포인터처럼 동작하는 클래스 템플릿으로, 사용이 끝난 메모리를 자동으로 해제해준다.

즉 delete를 자동으로 수행해준다.

 

 


 

 

스마트 포인터의 동작

 

보통 new 키워드를 사용해 기본 포인터가 실제 메모리를 가리키도록 초기화한 후,

기본 포인터를 스마트 포인터에 대입하여 사용한다.

 

이렇게 정의된 스마트 포인터의 수명이 다하면, 소멸자는 delete 키워드를 사용하여 할당된 메모리를 자동으로 해제한다.

따라서 new 키워드가 반환하는 주소값을 스마트 포인터에 대입하면, 따로 메모리를 해제할 필요가 없어지게 되는 것이다.

 

이를 소스코드로 나타내면 아래와 같다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>
 
 
using namespace std;
 
 
class Pointer
{
public:
    Pointer(int X, int Y) : x(X), y(Y)
    {
    }
 
    void print()
    {
        cout << x << ", " << y << "\n";
    }
 
private:
    int x;
    int y;
};
 
class SmartPtr
{
public:
    SmartPtr(Pointer* p) : ptr(p) 
    {
    }
 
    ~SmartPtr()
    {
        cout << "\n소멸 호출 : ";
        ptr->print();
        delete ptr;
    }
 
    //연산자 오버로딩
    Pointer* operator->()
    {
        return ptr;
    }
 
private:
    Pointer* ptr;
};
 
int main(void)
{
    SmartPtr p1 = new Pointer(12);
    SmartPtr p2 = new Pointer(34);
 
    p1->print();
    p2->print();
 
    return 0;
};
 
cs

실행 결과

 


 

 

스마트 포인터의 종류

 

C++11 이전에는 auto_ptr이라는 스마트 포인터를 사용하였지만,

C++11부터는 다음과 같은 새로운 스마트 포인터를 제공하고 있다.

 

1. unique_ptr

2. shared_ptr

3. weak_ptr

 

이와 같은 스마트 포인터는 memory 헤더 파일에 정의되어 있다.

 

 


 

unique_ptr

 

unique_ptr은 하나의 스마트 포인터만이 특정 객체를 소유할 수 있도록

객체에 소유권을 도입한 스마트 포인터이다.

 

이 스마트 포인터는 해당 객체의 소유권을 가지고 있을 때만 소멸자가 해당 객체를 삭제할 수 있다.

unique_ptr 인스턴스는 move() 멤버 함수를 통해 소유권을 이전할 수 있지만, 복사는 불가능하다. 

또 소유권이 이전되면 이전 인스턴스는 해당 객체를 소유하지 않게 재설정된다.

(대입 연산자를 이용한 복사는 불가능한다.)

 

보통의 C++ 객체에 대해 스마트 포인터가 필요한 상황에서는 주로 unique_ptr를 사용하면 된다.

 

또 C++14 이후 make_unique() 함수를 제공하며 이를 통해 unique_ptr 인스턴스를 안전하게 생성할 수 있다.

make_unique() 함수는 전달받은 인수를 사용해 지정된 타입의 객체를 생성하고, 생성된 객체를 가리키는 unique_ptr를 반환한다.

이 함수를 사용하면 예외 발생에 대해 안전하게 대처가 가능해진다.

 

make_unique() 함수 사용을 위해서는 컴파일러가 c++14를 지원하여야 한다.

 

생성)

unique_ptr<객체> 스마트 포인터명(new 객체)

unique_ptr<객체> 스마트 포인터명 = make_unique<객체>(인수);

 

 


 

shared_ptr

 

shared_ptr은 하나의 특정 객체를 참조하는 스마트 포인터가 총 몇 개인지를 참조하는 스마트 포인터이다.

이렇게 참조하고 있는 스마트 포인터의 개수를 참조 횟수 (reference count)라고 한다.

참소 횟수는 특정 객체에 새로운 shared_ptr이 추가될 때마다 1씩 증가하며, 수명이 다할 때마다 1씩 감소한다.

따라서 마지막 인스턴스의 수명이 다하여 참조 횟수가 0이 되면 delete 키워드를 사용하여 메모리를 자동으로 해제한다.

 

참조 횟수(참조 카운트)는 use_count() 멤버함수를 통해 알 수 있다.

 

생성)

shared_ptr<객체> 포인터명(new 객체);

shared_ptr<객체> 포인터명 = make_shared<객체>(인수);

 

 


 

weak_ptr

 

weak_ptr은 하나 이상의 shared_ptr 인스턴스가 소유하는 객체에 대한 접근을 제공하지만,

소유자의 수에는 포함되지 않는 스마트 포인터이다.

 

shared_ptr은 앞의 설명과 같이 참조 횟수(reference count)를 기반으로 동작하는 스마트 포인터이다.

만약 서로가 상대방을 가리키는 shared_ptr을 가지고 있다면, 참조 횟수는 절대 0이 되지 않으므로 메모리는 영원히 해제되지 않는다.

이러한 상황을 순환 참조 ((circular reference)라고 하며 weak_ptr은 이런 순환 참조를 제거하기 위해 사용된다.

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

[C++] C++의 Casting  (1) 2024.01.25
[C++] 실시간 타입 정보 (RTTI, Type_info)  (0) 2024.01.25
[C#] 가비지 컬렉터  (0) 2021.11.22
[C++/STL] algorithm헤더의 sort함수  (0) 2021.11.09
[C++] Pointer에 관하여  (0) 2021.10.26

+ Recent posts