본문 바로가기

개발/C++

C++ 스마트 포인터

 

기본적인 포인터

#include <iostream>

int main()
{
	int *rawPtr = new int(10);
	std::cout << *rawPtr << std::endl;
	delete rawPtr;
}

기본 포인터는 동적으로 메모리를 할당해주고

 

delete로 반드시 메모리 해제를 해줘야 함

 

보통 new / delete를 세트로 잘해주겠지만

 

프로젝트를 진행하다 보면 동적 할당하는 곳과 delete 하는 곳이 달라져

 

문제가 발생하는 경우가 생김

 

 

스마트 포인터는 3가지 종류가 있음

- shared_ptr

- unique_ptr

- weak_ptr

 

 

shared_ptr

 

shared_ptr는 내부에 레퍼런스 카운트가 존재하고

 

레퍼런스 카운트가 0이 될 때 메모리에서 해제됨

 

위 코드를 보면 sharedPtr을 2, 3이 추가로 참조하고 있음

 

이러면 본인을 포함해 총 3의 레퍼런스 카운트를 가지게 됨

#include "pch.h"
#include <iostream>
#include <memory>

int main()
{
	std::shared_ptr<int> sharedPtr = std::shared_ptr<int>(new int(10));
	std::cout << *sharedPtr << " " << sharedPtr.use_count() << std::endl;
	std::shared_ptr<int> sharedPtr2 = sharedPtr;
	std::cout << *sharedPtr2 << " " << sharedPtr.use_count() << " " << sharedPtr2.use_count() << std::endl;
	std::shared_ptr<int> sharedPtr3 = sharedPtr;
	std::cout << *sharedPtr3 << " " << sharedPtr.use_count() << " " << sharedPtr2.use_count() << " " << sharedPtr3.use_count() << std::endl;
}

결과

sharedPtr, 2, 3이 모두 같은 레퍼런스 카운트를 공유함

 

shared_ptr을 쓰다 보면 순환 참조 문제가 발생할 수 있음

 

 

 

unique_ptr

 

unique_ptr는 유일하게 존재해야 할 포인터를 위함

 

unique_ptr의 생성자를 확인하면 복사 생성자와 대입 연산자가 삭제돼있음

unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;

 

unique_ptr는 std::move를 이용해 소유권을 넘겨야 함

#include "pch.h"
#include <iostream>
#include <memory>

int main()
{
	std::unique_ptr<int> uniquePtr = std::unique_ptr<int>(new int(10));
	std::unique_ptr<int> uniquePtr2 = std::move(uniquePtr);

	std::cout << *uniquePtr2 << std::endl;
	std::cout << *uniquePtr << std::endl; // 에러 발생
}

결과

소유권이 넘어가 uniquePtr은 empty가 됨

 

uniquePtr에 접근하면 에러가 발생함

 

 

weak_ptr

 

weak_ptr는 레퍼런스 카운트를 올리지 않음

#include "pch.h"
#include <iostream>
#include <memory>

int main()
{
	std::shared_ptr<int> sharedPtr = std::shared_ptr<int>(new int(10));
	std::shared_ptr<int> sharedPtr2 = sharedPtr;
	std::cout << *sharedPtr << " " << sharedPtr.use_count() << std::endl;

	std::weak_ptr<int> weakPtr = sharedPtr;
	std::cout << *weakPtr.lock() << " " << weakPtr.use_count() << std::endl;
}

 

weakPtr은 lock함수로 참조하고 있는 포인터를 가져옴

 

lock함수는 shared_ptr을 리턴하고 사용하는 동안 레퍼런스 카운트가 1 오름