λ°˜μ‘ν˜•

RAII(Resource Acquisition Is Initialization) - SBRM(Scope Bound Resource Managemnet)

가비지 컬렉터(Garbage Collector)κ°€ μžˆλŠ” C#κ³Ό 달리, C++ ν”„λ‘œκ·Έλž˜λ°μ—μ„œ μžμ›μ˜ κ΄€λ¦¬λŠ” μ „μ μœΌλ‘œ 개발자의 μ±…μž„μž…λ‹ˆλ‹€. νž™(Heap) μ˜μ—­μ—μ„œ μ–΄λ–€ 핸듀을 μƒμ„±ν–ˆμœΌλ©΄ λ°˜λ“œμ‹œ ν•΄μ œν•˜μ—¬ λ©”λͺ¨λ¦¬ μ‚¬μš©μ„ λ°˜ν™˜ν•΄μ•Ό ν•˜λŠ” 것이죠. λ§Œμ•½, 그렇지 μ•Šλ‹€λ©΄ λ©”λͺ¨λ¦¬ λˆ„μˆ˜κ°€ λ°œμƒν•˜κ±°λ‚˜ 예츑 λΆˆκ°€λŠ₯ν•œ 상황에 직면할 수 μžˆμŠ΅λ‹ˆλ‹€.

μ—¬κΈ°μ„œ μžμ›(Resource)μ΄λΌλŠ” 것은 ν”νžˆ μ–˜κΈ°ν•˜λŠ” μ‚¬μš©μž 버퍼 곡간이 될 μˆ˜λ„ 있고, μ†ŒμΌ“, 파일, GDI λ“± μ–΄λ–€ 객체의 핸듀이 될 μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. μ΄λŸ¬ν•œ μžμ›μ„ μƒμ„±ν•˜κ³ , μŠ€μ½”ν”„ μ˜μ—­(Scope bound)을 λ²—μ–΄λ‚˜λ©΄ μžλ™μœΌλ‘œ ν•΄μ œν•˜λŠ” κ°œλ…μ„ RAII라고 ν•©λ‹ˆλ‹€. RAIIλ₯Ό λ””μžμΈ νŒ¨ν„΄(Design pattern) λ˜λŠ” κ΄€μš©κ΅¬(Idiom)라고도 ν•˜μ£ .

참고둜, RAII 이름이 κ°œλ…μ— λΉ„ν•΄ μ μ ˆν•˜μ§€ μ•Šλ‹€λŠ” 의견이 μžˆμŠ΅λ‹ˆλ‹€. RAII κ°œλ…μ˜ 핡심은 파괴(ν•΄μ œ)인데, 이름은 μ΄ˆκΈ°ν™”μ΄κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€. κ·Έλž˜μ„œ SBRM(Scope Bound Resource Management)λΌλŠ” 이름이 생긴 것이죠. SBRM은 의미 κ·ΈλŒ€λ‘œ, μŠ€μ½”ν”„ μ˜μ—­μ—μ„œμ˜ μžμ› 관리λ₯Ό λœ»ν•©λ‹ˆλ‹€. μ’€ 더 μ΄ν•΄ν•˜κΈ°κ°€ 쉽죠?

μ•„λž˜ λ§ν¬λŠ” RAII 이름에 λŒ€ν•œ λ²ˆμ—­κ°€ λ₯˜κ΄‘λ‹˜μ˜ 해석인데, ν•΄λ‹Ή 링크도 읽어보면 쒋을 것 κ°™μŠ΅λ‹ˆλ‹€.

https://occamsrazr.net/tt/297

 

C++ RAII(Resource Acquisition Is Initialization)의 해석

RAIIλΌλŠ” 이름 μžμ²΄μ— λŒ€ν•œ "κΏˆλ³΄λ‹€ ν•΄λͺ½"

occamsrazr.net

RAIIκ°€ ν•„μš”ν•œ 상황

κ·Έλ ‡λ‹€λ©΄, RAIIλΌλŠ” 것이 μ–΄λ–€ μƒν™©μ—μ„œ ν•„μš”ν•œ κ²ƒμΌκΉŒμš”? λͺ‡ 가지 μ˜ˆμ‹œλ₯Ό λ“€μ–΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.

mutexκ°€ μ•ˆμ „ν•˜κ²Œ ν•΄μ œλ˜μ§€ μ•ŠλŠ” 경우

μ•„λž˜ μ½”λ“œλŠ” doSomething() ν•¨μˆ˜μ— μ§„μž…ν•˜λ©΄ mutex둜 lock이 κ±Έλ € λ‹€λ₯Έ λ¦¬μ†ŒμŠ€μ˜ 접근을 λ§‰κ²Œ 되고, 이후 μ˜ˆμ™Έκ°€ λ°œμƒν•˜μ—¬ κ°€μž₯ κ·Όμ ‘ν•œ try-catch 문으둜 λΉ μ Έλ‚˜κ°€λŠ” λ™μž‘μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€.

#include <mutex>

std::mutex mutex;

void doSomething()
{
	mutex.lock();

	throw std::exception("μ˜ˆμ™Έ λ°œμƒ!");

	mutex.unlock();
}

int main()
{
	try
	{
		doSomething();
	}
	catch (const std::exception& exc)
	{
		std::cout << exc.what() << std::endl;
	}
}

즉, unlock이 ν˜ΈμΆœλ˜μ§€ μ•Šμ€ 채 λΆˆμ•ˆμ •ν•˜κ²Œ ν•¨μˆ˜λ₯Ό λΉ μ Έλ‚˜κ°€λŠ” μƒν™©μž…λ‹ˆλ‹€. μ΄λ ‡κ²Œ 잠금이 ν•΄μ œλ˜μ§€ μ•Šμ€ 채 ν”„λ‘œκ·Έλž¨μ΄ μ’…λ£Œλ˜κ±°λ‚˜, λ‹€λ₯Έ κ³³μ—μ„œ λ‹€μ‹œ lock을 ν˜ΈμΆœν•˜λ©΄ 였λ₯˜κ°€ λ°œμƒν•©λ‹ˆλ‹€.

이 κ²½μš°μ—λŠ” lock_guardλ₯Ό μ‚¬μš©ν•˜μ—¬ μ•ˆμ „ν•˜κ²Œ mutexλ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

#include <mutex>

std::mutex mutex;

void doSomething()
{
	std::lock_guard<std::mutex> g(mutex);

	throw std::exception("μ˜ˆμ™Έ λ°œμƒ!");
}

int main()
{
	try
	{
		doSomething();
	}
	catch (const std::exception& exc)
	{
		std::cout << exc.what() << std::endl;
	}
}

lock_guardλŠ” RAII μŠ€νƒ€μΌ λ©”μ»€λ‹ˆμ¦˜μ΄ 적용된 νŽΈλ¦¬ν•œ mutex wrapper 클래슀둜, lock_guard 객체가 μƒμ„±λ˜λ©΄ mutex μ†Œμœ κΆŒμ„ μ–»μœΌλ €κ³  μ‹œλ„ν•˜κ³ , μŠ€μ½”ν”„ μ˜μ—­μ„ λ²—μ–΄λ‚˜λ©΄ 객체가 파괴되고 ν•΄μ œλ©λ‹ˆλ‹€. μžμ„Έν•œ μ„€λͺ…은 μ•„λž˜ 링크λ₯Ό μ°Έκ³ ν•˜μ„Έμš”.

https://en.cppreference.com/w/cpp/thread/lock_guard

 

std::lock_guard - cppreference.com

template< class Mutex > class lock_guard; (since C++11) The class lock_guard is a mutex wrapper that provides a convenient RAII-style mechanism for owning a mutex for the duration of a scoped block. When a lock_guard object is created, it attempts to take

en.cppreference.com

μ‚¬μš©μž λ©”λͺ¨λ¦¬κ°€ μ•ˆμ „ν•˜κ²Œ ν•΄μ œλ˜μ§€ μ•ŠλŠ” 경우

μ‚¬μš©μž λ©”λͺ¨λ¦¬λ₯Ό μƒμ„±ν•˜κ³  μ œκ±°ν•˜κΈ° μœ„ν•΄ new와 deleteλ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€. μ•„λž˜ μ˜ˆμ‹œλŠ”, μ‚¬μš©μž λ©”λͺ¨λ¦¬λ₯Ό μƒμ„±ν–ˆλŠ”λ° μ–΄λ– ν•œ 이유둜 deleteλ₯Ό μ •μƒμ μœΌλ‘œ ν˜ΈμΆœν•˜μ§€ μ•Šμ•„ λ©”λͺ¨λ¦¬ λˆ„μˆ˜κ°€ κ°μ§€λ˜λŠ” μƒν™©μž…λ‹ˆλ‹€.

#define _CRTDBG_MAP_ALLOC
#include <cstdlib>
#include <crtdbg.h>

#ifdef _DEBUG
#define new new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
#endif

void doSomething()
{
	unsigned char* buffer = new unsigned char[100];

	throw std::exception("μ˜ˆμ™Έ λ°œμƒ!");

	delete buffer;
	buffer = nullptr;
}

int main()
{
	try
	{
		doSomething();
	}
	catch (const std::exception& exc)
	{
		std::cout << exc.what() << std::endl;
	}

	_CrtDumpMemoryLeaks();
}

μœ„ μ½”λ“œλ₯Ό μ‹€ν–‰ν•˜λ©΄ μ•„λž˜μ™€ 같이 λ©”λͺ¨λ¦¬ λˆ„μˆ˜κ°€ κ°μ§€λ©λ‹ˆλ‹€. ν•΄λ‹Ή λ©”μ‹œμ§€λ₯Ό 더블 ν΄λ¦­ν•˜λ©΄ μ •ν™•νžˆ 11번째 λΌμΈμ—μ„œ λ©”λͺ¨λ¦¬ λˆ„μˆ˜κ°€ μžˆμŒμ„ μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€.

λ©”λͺ¨λ¦¬-λˆ„μˆ˜κ°€-κ°μ§€λœ-κ²°κ³Ό
λ©”λͺ¨λ¦¬ λˆ„μˆ˜κ°€ κ°μ§€λœ κ²°κ³Ό

μœ„ μ˜ˆμ‹œμ˜ CRTDBGλ₯Ό μ‚¬μš©ν•˜λŠ” 방법은 μ•„λž˜ 링크λ₯Ό μ°Έκ³ ν•˜μ„Έμš”.

[디버깅] λ©”λͺ¨λ¦¬ λˆ„μˆ˜λ₯Ό μ κ²€ν•˜λŠ” 방법 - CRTDBG (예제 포함)

 

[디버깅] λ©”λͺ¨λ¦¬ λˆ„μˆ˜λ₯Ό μ κ²€ν•˜λŠ” 방법 - CRTDBG (예제 포함)

λ©”λͺ¨λ¦¬ λˆ„μˆ˜μ™€ κ΄€λ ¨λœ ν¬μŠ€νŒ…μ€ μ•„λž˜ 링크λ₯Ό μ°Έκ³ ν•΄μ£Όμ„Έμš”. [디버깅] λ©”λͺ¨λ¦¬ λˆ„μˆ˜μ™€ μ κ²€ν•˜λŠ” 방법 μ†Œκ°œ [디버깅] λ©”λͺ¨λ¦¬ λˆ„μˆ˜μ™€ μ κ²€ν•˜λŠ” 방법 μ†Œκ°œ μ†Œκ°œ Unmanaged Code인 C++은 λ©”λͺ¨λ¦¬ ν• λ‹Ήκ³Ό ν•΄μ œ

luckygg.tistory.com

μ΄λ ‡κ²Œ μ‚¬μš©μž λ©”λͺ¨λ¦¬λ₯Ό μ•ˆμ „ν•˜κ²Œ μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” 슀마트 포인터(Smart pointer)λ₯Ό μ‚¬μš©ν•  것을 ꢌμž₯ν•©λ‹ˆλ‹€.

#include <vector>
#include <memory>

#define _CRTDBG_MAP_ALLOC
#include <cstdlib>
#include <crtdbg.h>

#ifdef _DEBUG
#define new new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
#endif

void doSomething()
{
	std::unique_ptr<std::vector<unsigned char>> buffer = std::make_unique<std::vector<unsigned char>>(100);
	
	throw std::exception("μ˜ˆμ™Έ λ°œμƒ!");
}

int main()
{
	try
	{
		doSomething();
	}
	catch (const std::exception& exc)
	{
		std::cout << exc.what() << std::endl;
	}

	_CrtDumpMemoryLeaks();
}

λ‹€μ†Œ λ³΅μž‘ν•΄ λ³΄μ΄λŠ”λ°μš”. unsigned charλ₯Ό vector둜 κ΅¬μ„±ν•˜κ³ , 이λ₯Ό unique_ptr둜 κ΄€λ¦¬ν•˜λŠ” λ°©μ‹μž…λ‹ˆλ‹€. 이 경우, μ•„λž˜μ™€ 같이 직접 포인터 μ£Όμ†Œμ— μ ‘κ·Όν•  수 μžˆμŠ΅λ‹ˆλ‹€.

unsigned char* ptr = buffer->data();

μ΄λ ‡κ²Œ 슀마트 포인터λ₯Ό μ‚¬μš©ν•˜λ©΄ 직접 λ©”λͺ¨λ¦¬λ₯Ό ν•΄μ œν•˜μ§€ μ•Šμ•„λ„ μžλ™μœΌλ‘œ 관리해 μ€λ‹ˆλ‹€. 슀마트 포인터에 λŒ€ν•œ μžμ„Έν•œ μ„€λͺ…은 μ•„λž˜ 링크λ₯Ό μ°Έκ³ ν•˜μ„Έμš”.

https://en.cppreference.com/book/intro/smart_pointers

 

smart pointers - cppreference.com

Warning: This wiki is part of the deprecated and unmaintained CppReference Book project. For up-to-date information on C++, see the main reference at cppreference.com. Smart pointers are used to make sure that an object is deleted if it is no longer used (

en.cppreference.com

λ°˜μ‘ν˜•