C++의 가장 큰 장점이자 단점이라고 하면 대부분의 사람들이 공통으로 꼽는 것이 바로 '포인터의 사용'이다.

C++은 포인터를 이용하여 메모리를 직접 관리하고, 따라서 타 언어에 비해 훨씬 성능이 좋다.


하지만 포인터는 양날의 검이다. 잘 다루면 성능 좋은 프로그램을 만들 수 있지만 메모리 관리를 잘못하는 순간 프로그램이 박살날 수 있다.


 C++ 개발자들은 이런 메모리 관리 문제를 어떻게 해결하면 좋을지 생각하다가 '스마트 포인터'라는 개념을 만들었다.

이것을 이용해 메모리를 할당하면 메모리 관리를 자동으로 해 주는 아주 편리한 도구인 것이다.


 초기 스마트 포인터는 auto_ptr 이라는 예약어를 이용하여 할당할 수 있었다.

auto_ptr로 할당한 메모리는 소유권이라는 것을 가지고 있어, 이를 가리키고 있던 포인터가 사라지면 메모리를 자동으로 해제해 주는 편리함을 제공했다.

하지만 auto_ptr에는 여러가지 문제점이 있었는데, 바로 '소유권 이전'에 관한 문제였다.


 auto_ptr로 생성한 포인터는 다른 포인터에 복사할 경우 소유권을 이전하게 되는데, 복사한 포인터가 사라질 경우 할당된 메모리를 자동으로 해제해버리는 문제가 발생한다.

간단한 예제로 다음과 같은 코드를 돌려보자.


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
#include "stdafx.h"
#include "memory"  
#include "iostream"  
 
class MyClass
{
public:
    int num;
    MyClass()
    {
        std::cout << "Ctor" << std::endl;
    }
 
    ~MyClass()
    {
        std::cout << "Dtor" << std::endl;
    }
};
 
void print_num(std::auto_ptr<MyClass> myClass)
{
    std::cout << myClass->num << std::endl;
}
 
int _tmain(int argc, _TCHAR* argv[])
{
    std::auto_ptr<MyClass> myClass(new MyClass);
    myClass->num = 1234;
    std::cout << "print start" << std::endl;
    print_num(myClass);
    std::cout << "print ended" << std::endl;
    std::cout << myClass->num << std::endl;
    return 0;
}
 
cs




 아름답게 박살이 나는 프로그램의 모습을 볼 수 있다.

출력된 것을 보면 알겠지만 print_num 함수 내부의 매개변수로 main의 myClass가 복사되면서 소유권이 이전되었고, print_num 함수가 끝나면서 myClass가 할당된 메모리가 해제되어 main에서 해제된 메모리를 참조하려다 에러가 나는 것이다.


 이런 auto_ptr의 단점을 보완한 것이 shared_ptr이다.

본래 boost 라이브러리에 속해있던 기능이지만 C++11에서 표준으로 포함되었다.

auto_ptr과 달리 shared_ptr은 레퍼런스 카운팅 방식으로 메모리를 관리하기 때문에 해당 인스턴스를 가리키는 포인터가 전부 사라져야 메모리가 해제된다.


auto_ptr을 shared_ptr로 바꾼 코드를 실행시켜보자.


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
#include "stdafx.h"
#include "memory"  
#include "iostream"  
 
class MyClass
{
public:
    int num;
    MyClass()
    {
        std::cout << "Ctor" << std::endl;
    }
 
    ~MyClass()
    {
        std::cout << "Dtor" << std::endl;
    }
};
 
void print_num(std::shared_ptr<MyClass> myClass)
{
    std::cout << myClass->num << std::endl;
}
 
int _tmain(int argc, _TCHAR* argv[])
{
    std::shared_ptr<MyClass> myClass(new MyClass);
    myClass->num = 1234;
    std::cout << "print start" << std::endl;
    print_num(myClass);
    std::cout << "print ended" << std::endl;
    std::cout << myClass->num << std::endl;
    return 0;
}
 
cs




 이렇게 정상 작동하는 것을 확인할 수 있다.

이 외에도 스마트 포인터에는 unique_ptr과 weak_ptr이 존재하는데, 이는 다음번에 공부하면서 작성하도록 하겠다.

블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

,