WinCNT

스마트 포인터 본문

게임 프로그래밍(학습 내용 정리)/Modern C++

스마트 포인터

WinCNT_SSS 2022. 4. 14. 12:48

메모리를 동적 할당할 경우는 반드시 delete를 해줘야 한다

 

문제는 Block 안(특히 함수 등)에서 메모리를 동적 할당할 경우이다

블록을 벗어나면 포인터 변수에는 접근할 수 없으니 해제가 불가능해진다

심지어 new-delete를 제대로 해줬다고 해도 다음과 같이 throw가 있으면 문제가 발생할 수 있다

void foo()
{
	double* pDouble { new double };
    *pDouble = 3.14;
    throw...; //
    delete pDouble;
}

malloc과 new의 차이

malloc - free의 경우 free할 때 소멸자가 호출되지 않는다

new - delete의 경우 delete할 때 자동으로 소멸자가 호출된다

 

즉, 객체(클래스) 생성에는 반드시 new - delete를 써야한다

포인터의 문제점

포인터가 가리키는 주소의 값이 배열인지 하나의 객체인지 판별 불가능 --> 포인터 선언의 타입

파괴하기 위해 어떤 방식을 써야할지 알아낼 방식이 없다 --> delete? delete[]??

포인터가 가리키는 대상을 내가 소유하는지 아닌지 알 수 없다 --> 배타적 소유권

댕글링 포인터(Dangling pointer), 자원이 이미 해제된 자원인지 아닌지 판별할 수 없다 --> nullptr

자원 해제는 정확히 한 번만 해야한다 --> 중복해제 방지

// 재할당
while(true)
{
	int* ptr = new int;		// 기존의 new int에 접근할 수 없게 된다
}

 

위의 문제점을 해결하기 위해 C++11에서는 스마트 포인터가 등장했다

스마트 포인터의 개요

<memory> 헤더 파일에 존재

C++에서는 메모리 누수로부터 프로그램의 안전성을 보장하기 위해 스마트 포인터를 제공

 

스마트 포인터란 포인터처럼 동작하는 클래스 템플릿으로

사용이 끝난 메모리를 자동으로 해제해준다

unique_ptr<> : 배타적 소유권
shared_ptr<> : 공유 자원 관리
weak_ptr<>   : shared_ptr를 보완하기 위해 사용

unique_ptr의 예

class Foo
{
public:
	Foo() { cout << "생성\n"; }
	Foo(int) { cout << "생성(int)\n"; }
	~Foo() { cout << "소멸\n"; }

	void func() {}
};

int main()
{
	Foo* ptr = new Foo();
	delete ptr;

	// 내가 생성한 객체를 스마트 포인터가 관리하라는 느낌
	unique_ptr<Foo> smartPtr1(new Foo());				// C++11
	// 위의 코드를 보완한 것
	unique_ptr<Foo> smartPtr2 = make_unique<Foo>();	// C++14
	auto smartPtr3 = make_unique<Foo>();

	cout << "중괄호 start\n";
	{
		auto smartPtr4 = make_unique<Foo>();
	}
	cout << "중괄호 end\n";


	return 0;
}

RAII(Resource Acquisition Is Initialize) 패턴

객체 생성 시에 할당한 메모리에 대해서

소멸자 호출 시에 해제하도록 하면 메모리 관리가 수월해진다!

/// <summary>
/// RAII 패턴 예제
/// </summary>
class MyString
{
public:
	char* c = nullptr;
	MyString(size_t len) { c = new char[len]; }
	~MyString() { delete[] c; }
};

스마트 포인터는 다음과 같이 만들 수 있다

class MyString
{
public:
	char* c = nullptr;
	MyString(size_t len)
	{
		cout << "MyString 자원 획득함!\n";
		c = new char[len];
	}
	~MyString()
	{
		cout << "MyString 자원 delete!\n";
		delete[] c;
	}

	void func() { cout << c << endl; }
};

class MyString_SPtr
{
	MyString* data;
public:
	MyString_SPtr(MyString* d):data(d) { }
	~MyString_SPtr() { delete data; }

	MyString& operator*() const { return *data; }
	MyString* operator->() const { return data; }
};

unique_ptr는 위의 클래스를 템플릿화한 느낌의 std이다

 

SSS