개발자 면접 공부/C-C++

C++ 스마트 포인터(Unique, Shared, Weak)

chogyujin 2022. 7. 19. 23:41
728x90

오늘은 스마트 포인터에 대해 공부해보겠습니다.


스마트 포인터 란?

자바, C# 등은 가비지 컬렉터(Garbage Collector) 라는 기능을 통해 메모리를 관리합니다.
하지만 C++는 사용자가 스스로 메모리를 할당 해제를 통해 관리를 해줘야합니다. C에서는 malloc free
C++에서는 new delete를 통해서 말이지요 하지만 사용자의 실수로 인해 메모리를 해제를 안할경우
프로그램은 계속 메모리를 사용하는걸로 인지하기 떄문에 해당 메모리를 정확히는 메모리 한칸을 차지하면서
사용을 안하고있는 상태가 됩니다. 이는 즉시 메모리 누수(memory leak)이 발생합니다.
이를 방지 하기위해 스마트 포인터라는것을 제공해주며 스마트 포인터는 포인터 처럼 사용하는 클래스 템플릿
입니다. 메모리를 자동으로 해제를 해주기 때문에 관리하기가 매우 용이해집니다.


스마트 포인터의 종류
스마트 포인터의 기능은 C++11 이후 부터 추가 되었습니다. memory헤더 파일을 include를 해야 사용이 가능합니다.

1. unique_ptr
unique_ptr은 하나의 스마트 포인터만이 객체를 가리킬수가 있습니다. 단 move를 통해 이주는 가능

#include<iostream>
#include<memory>
using namespace std;

class Car
{
public : 
	virtual void Open()
	{
		cout << "차문 개방" << endl;
	}
	~Car()
	{
		cout << "기본 Base 부모 클래스 해제" << endl;
	}
};

class BasicCar : public Car
{
public:
	void Open()
	{
		cout << "옆으로 열림" << endl;
	}
	~BasicCar()
	{
		cout << "기본카 해제" << endl;
	}
};

class SuperCar : public Car
{
public:
	void Open()
	{
		cout << "위로 열림" << endl;
	}
	~SuperCar()
	{
		cout << "슈퍼카 해제" << endl;
	}
};

int main()
{
	unique_ptr<SuperCar> S(new SuperCar());
	S->Open();
}

unique_ptr은 단 하나의 객체를 가질수밖에 없습니다. 이런 경우 에러가 발생합니다.

unique_ptr<SuperCar> S(new SuperCar());
S->Open();
unique_ptr<SuperCar> S1 = S;//에러

하지만 move함수를 사용하면 어떻게될가요?

unique_ptr<SuperCar> S(new SuperCar());
S->Open();
unique_ptr<SuperCar> S1 = move(S);
S1->Open();

정상적으로 작동이 됩니다. move함수는 원래 가지고있던 S객체를 S1에 이동을 해준거뿐입니다.
그럼 S는 S1의 move를 통해 이동이 되었으니 어떻게 되었을가요?

empty 즉 사라졌습니다. 따라서 S를 통해 어떠한 함수나 변수를 변경 및 실행이 불가능해집니다.

unique_ptr<SuperCar> S = make_unique<SuperCar>();

이러한 방법으로도 만들수 있습니다.


2. shared_ptr
shared_ptr은 어떤 하나의 객체를 참조하는 스마트 포인터의 개수를 참조하는 스마트 포인터입니다. 참조하고 있는
스마트 포인터의 개수를 참조 카운트 라고 합니다. shared_ptr은 추가될때마다 1씩 증가가 되고 수명이 다하면
1씩 감소를 하게 됩니다. 마지막 shared_ptr이 수명이 다하는 순간 참조 카운트가 0이 되여 자동으로 소멸자 호출후
해제를 합니다.

#include<iostream>
#include<memory>
using namespace std;

class Car
{
public : 
	virtual void Open()
	{
		cout << "차문 개방" << endl;
	}
	~Car()
	{
		cout << "기본 Base 부모 클래스 해제" << endl;
	}
};

class BasicCar : public Car
{
public:
	void Open()
	{
		cout << "옆으로 열림" << endl;
	}
	~BasicCar()
	{
		cout << "기본카 해제" << endl;
	}
};

class SuperCar : public Car
{
public:
	void Open()
	{
		cout << "위로 열림" << endl;
	}
	~SuperCar()
	{
		cout << "슈퍼카 해제" << endl;
	}
};

int main()
{
	shared_ptr<SuperCar> S(new SuperCar());
	cout <<"참조 값 S: " << S.use_count() << endl;
	auto S1 = S;
	cout << "참조 값 S : " << S.use_count() << endl;
	cout << "참조 값 S1 : " << S1.use_count() << endl;

	shared_ptr<SuperCar> S2 = S1;
	cout << "참조 값 S1 : " << S1.use_count() << endl;
	cout << "참조 값 S2 : " << S2.use_count() << endl;
}

use_count 함수는 현재 shared_ptr 객체가 가리키고 있는 객체를 참조 중인 소유자의 수를 반환해줍니다.
S는 현재 1 S1에게 S에 가르키는 객체를 알려주었더니 S, S1 또한 수가 2로 되었고 마찬가지로 아래에 S2가 S1을 가르키는 객체를 알려주면 3으로 증가된모습을 볼수있습니다

.세개의 변수가 같은 주소를 가르키는 것을 확인할수 있습니다.

shared_ptr<SuperCar> S = make_shared<SuperCar>();

또한 unique_ptr처럼 make를 사용하여 할당을 할수 있습니다.


3.weak_ptr
weak_ptr은 하나 이상의 shared_ptr가 가르키는 객체를 참조할수 있지만 참조 횟수를 늘리지 않는 스마트 포인터입니다.
shared_ptr를 사용할때 최대의 문제점은 서로가 서로를 가르키는 경우 참조 횟수가 절대로 0이 될수가 없으므로
메로리가 해제되지 않는 순환 참조가 발생하게 됩니다. weak_ptr은 이러한 순환 참조를 제거하기 위해 사용됩니다.

'개발자 면접 공부 > C-C++' 카테고리의 다른 글

(C++) 이동생성자(Move Constructor)  (0) 2022.08.02
(C,C++) L-Value, R-Value  (0) 2022.08.02
C++ 캐스팅의 종류  (0) 2022.07.14
C++ RTTI 에 대해  (0) 2022.07.14
C++ 순수 가상 함수(추상 클래스)  (0) 2022.07.12