기존 C++ 포인터는 동적 할당 메모리를 생성(new) 해서 사용한 후 반드시 삭제(delete) 해줘야 한다.
프로그래머가 수동으로 관리하다보니 삭제가 누락되면 메모리 릭이 발생하곤 한다.
C++11 에서 추가된 Smart Pointer를 사용하면 메모리 삭제에 대한 부분을 신경쓰지 않아도 된다.
Smart Pointer에는 3가지(unique_ptr, shared_ptr, weak_ptr)가 있으며, weak_ptr은 사용 빈도가 매우 낮으므로 unique_ptr 과 shared_ptr 에 대해 알아보자.
1. 사용법
#include <memory>
std::unique_ptr< var-type > var;
std::shared_ptr< var-type > var;
(1) 인자
1-1) var-type : 변수나 객체의 타입을 지정
2. 일반 포인터 사용
- 포인터를 생성하여 사용한 후 반드시 삭제를 해줘야 한다.
- 프로그래머의 실수로 삭제가 누락될 경우 메모리 릭이 발생할 우려가 있다.
#include <iostream>
#include <string>
using Type = std::string;
int main()
{
Type* str{new Type("Hello world !!!")};
std::cout << *str << std::endl;
delete str; // 동적 메모리 사용 후 삭제 필요
return 0;
}
3. std::unique_ptr
- 소유권을 하나만 가질 수 있는 포인터이다.
- 객체를 선언하여 사용한 후 사용 범위가 종료될 때 소멸자가 호출되어 메모리를 자동으로 삭제한다.
- 소유권을 이전하기 위해서는 std::move()를 사용해야거나 참조로 전달해야 한다.
- C++14 이후에는 std::make_unique() 함수를 통해 포인터를 생성할 수 있다.
#include <iostream>
#include <string>
#include <memory>
using Type = std::string;
void print(std::unique_ptr<Type>& pStr4)
{
std::unique_ptr<Type> pStr5{std::move(pStr4)};
std::cout << pStr4 << std::endl; // 0
std::cout << pStr5 << std::endl; // 0x141fc20
// 함수가 종료되면 pStr5는 삭제됨.
}
int main()
{
std::unique_ptr<Type> pStr{new Type("Hello world !!!")};
//std::unique_ptr<Type> pStr{std::make_unique<Type>("Hello world !!!")}; // C++14 이후
std::cout << pStr << std::endl; // 0x141fc20
Type* ppStr{pStr.get()}; // 주소값 가져오기
std::cout << ppStr << std::endl; // 0x141fc20
//std::unique_ptr<Type> pStr2{pStr}; // 컴파일 에러!! (소유권은 하나만 가능)
std::unique_ptr<Type> pStr3{std::move(pStr)}; // 소유권 이전 가능
std::cout << pStr << std::endl; // 0
std::cout << pStr3 << std::endl; // 0x141fc20
print(pStr3); // 인자로 넘겨서 참조로 받기 가능
std::cout << pStr3 << std::endl; // 0
// 동적 메모리 사용 후 삭제가 필요하지 않음.
return 0;
}
4. std::shared_ptr
- 소유권을 여러개 가질 수 있는 포인터이다.
- 소유자 수의 관리는 내부적으로 참조 카운터(Reference Counter)를 통해 이루어진다.
- 참조 카운터가 0이 되면 메모리를 자동으로 삭제한다.
- C++14 이후에는 std::make_shared() 함수를 통해 포인터를 생성할 수 있다.
#include <iostream>
#include <string>
#include <memory>
using Type = std::string;
void print(std::shared_ptr<Type>& pStr3)
{
std::shared_ptr<Type> pStr4{std::move(pStr3)};
std::cout << pStr4 << " - " << pStr4.use_count() << std::endl; // 0x2274c20 - 2
pStr4.reset(); // 명시적으로 메모리 해제
// 함수가 종료되면 참조 카운트가 1 감소됨.
}
int main()
{
std::shared_ptr<Type> pStr{new Type("Hello world !!!")};
//std::shared_ptr<Type> pStr{std::make_shared<Type>("Hello world !!!")}; // C++14 이후
std::cout << pStr << " - " << pStr.use_count() << std::endl; // 0x2274c20 - 1
Type* ppStr{pStr.get()}; // 주소값 가져오기
std::cout << pStr << " - " << pStr.use_count() << std::endl; // 0x2274c20 - 1
std::shared_ptr<Type> pStr2{pStr}; // 소유권 여러개 가능
std::cout << pStr2 << " - " << pStr2.use_count() << std::endl; // 0x2274c20 - 2
print(pStr2); // 인자로 넘겨서 참조로 받기 가능
std::cout << pStr2 << " - " << pStr2.use_count() << std::endl; // 0 - 0
print(pStr);
std::cout << pStr << " - " << pStr.use_count() << std::endl; // 0 - 0
// 동적 메모리 사용 후 삭제가 필요하지 않음.
return 0;
}
'Programming Language > C++11' 카테고리의 다른 글
[c++11] std::tuple - 두 개 이상의 변수나 객체를 한번에 전달 (0) | 2024.07.02 |
---|---|
[c++11] std::pair - 두 개의 변수나 객체를 한번에 전달 (0) | 2024.07.02 |
[c++11] Lambda Function (람다 함수) - 익명 함수 (10) | 2024.06.26 |
[c++11] std::function - 함수 포인터를 대체하는 키워드 (9) | 2024.06.25 |
[c++11] std::atomic - 원자성을 보장해야 할 변수 정의 (26) | 2024.06.21 |