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이 존재하는데, 이는 다음번에 공부하면서 작성하도록 하겠다.
'Programming' 카테고리의 다른 글
[C++] Boost 설치 & 빌드 환경 구축 (0) | 2016.12.20 |
---|---|
[C++] unique_ptr, weak_ptr (0) | 2016.12.15 |
[C++] 알아두면 좋은(?) C++ 지식들 (0) | 2016.12.13 |
[C++] 얕은 복사, 깊은 복사, 복사 생성자 (0) | 2016.12.13 |
[C++] std::for_each (0) | 2016.12.12 |