C++은 메모리를 직접 관리할 수 있는 언어이다.
직접 관리는 자유를 주는 대신 책임도 따른다.
메모리를 직접 관리하면 실수에 의해 누수가 일어날 수 있다.
이를 막기 위해 가비지 컬렉션등 메모리 관리를 하는 알고리즘을 구현하거나 라이브러리등을 가져와야한다.
C++의 스마트 포인터를 이용하면 포인터를 사용하는 것 처럼 사용할 수 있으면서 (연산자 오버로딩)
메모리 관리는 스마트 포인터 객체에게 맡길 수 있다.
스마트 포인터 종류를 간단히 정리해보자.
보통 메모리 누수가 발생하는 이유는 해당 메모리를 바라보는 포인터들이 여러개일 경우
맨 마지막에 해당 메모리에 접근한 포인터가 메모리를 해제해야하는데 이를 관리하기가 쉽지 않다.
그래서 사용하는 전략이 대표적으로 두가지가 있는데
해당 메모리를 바라보는 포인터는 무조건 하나만 존재하게 만드는 고유 소유권 방식과
해당 메모리를 바라보는 포인터가 생기면 카운트를 올려주고 해제하면 카운트를 내려서
카운트가 모두 제거되었을때 실제 메모리를 제거해주는 레퍼런스 카운팅 방식이 있다.
다른 이유는 delete를 하기전에 예외등이 발생하여 delete가 실행이 안되는 경우이다.
이때는 스마트 포인터 객체로 감싸져 있기 때문에
예외가 발생했을때 해당 객체가 제거되고 메모리 해제부분이 실행되어 해결이 된다.
unique_ptr은 고유 소유권 방식의 스마트 포인터이다.
C++14부터 잘 지원되는 auto와 std::make_unique()를 이용하면 쉽게 unique_ptr을 생성할 수 있다.
make_unique()를 이용하면 생성자의 인자를 바로 넘겨줄 수 있어 간단하고
한번 감싸져 있는 형태여서 예외가 발생했을때 누수가 발생할 염려도 없다.
기본적인 사용법은 아래와 같다.
기본적으로 포인터와 같은 연산자를 이용할 수 있다.
get()을 통해 내부의 포인터를 직접 얻어올 수 있다.
reset()을 통해 내부 포인터를 해제하거나 내부 포인터를 다른 포인터로 교체할 수 있다.
release()는 내부 포인터를 돌려주며 소유권을 끊어주어서 직접 해제해야 한다.
class Test
{
public:
Test(int x) : x_(x) {};
void Print()
{
std::cout << x_ << std::endl;
}
private:
int x_;
};
void PrintTest(std::unique_ptr<Test> testPtr)
{
testPtr->Print();
}
int main()
{
auto myTest = std::make_unique<Test>(1);
myTest->Print();
PrintTest(std::make_unique<Test>(2));
myTest.get()->Print(); // internal pointer
myTest.reset(); // to nullptr
if (!myTest)
{
std::cout << "myTest is empty" << std::endl;
}
myTest.reset(new Test(3)); // to new pointer
myTest->Print();
auto myInternalTest = myTest.release();
if (!myTest)
{
std::cout << "myTest is empty" << std::endl;
}
myInternalTest->Print();
delete myInternalTest; // manually delete
return 0;
}
'프로그래밍 > C, C++' 카테고리의 다른 글
[C++14/17/20] 자주 사용할 것 같은 attribute만 간단 정리 (0) | 2021.10.05 |
---|---|
[C++20] std::format 사용해보기 (0) | 2021.09.24 |
[C++11/C++17] 유니폼 초기화 (Uniform Initialization) (0) | 2021.07.30 |
[C++17] string_view 간단 정리 (0) | 2021.05.07 |
More Effective C++ - 항목5 ~ 항목8 (0) | 2018.04.01 |