Resource Acquisition is Initialization (RAII)이란 무엇입니까?
Resource Acquisition is Initialization (RAII)이란 무엇입니까?
엄청나게 강력한 개념에 대한 끔찍한 이름이며 아마도 C ++ 개발자가 다른 언어로 전환 할 때 놓칠 수있는 가장 중요한 것 중 하나 일 것입니다. 이 개념을 Scope-Bound Resource Management 로 이름을 바꾸려는 시도가 조금 있었지만, 아직 이해가되지 않은 것 같습니다.
'자원'이라고 말하면 메모리를 의미하는 것이 아닙니다. 파일 핸들, 네트워크 소켓, 데이터베이스 핸들, GDI 객체 일 수 있습니다. 간단히 말해서, 우리는 유한 한 공급량을 가지고 있기 때문에 그들의 사용법을 통제하십시오. 'Scope-bound'측면은 객체의 수명이 변수의 범위에 묶여 있음을 의미하므로 변수가 범위를 벗어나면 소멸자가 리소스를 해제합니다. 이것의 매우 유용한 속성은 예외 안전성을 높이는 것입니다. 예를 들어, 이것을 비교하십시오 :
RawResourceHandle* handle=createNewResource();
handle->performInvalidOperation(); // Oops, throws exception
...
deleteResource(handle); // oh dear, never gets called so the resource leaks
RAII로
class ManagedResourceHandle {
public:
ManagedResourceHandle(RawResourceHandle* rawHandle_) : rawHandle(rawHandle_) {};
~ManagedResourceHandle() {delete rawHandle; }
... // omitted operator*, etc
private:
RawResourceHandle* rawHandle;
};
ManagedResourceHandle handle(createNewResource());
handle->performInvalidOperation();
후자의 경우 예외가 발생하고 스택이 풀리면 로컬 변수가 파괴되어 리소스가 정리되고 누출되지 않습니다.
이것은 프로그래밍 관용구입니다.
- 자원을 클래스로 캡슐화합니다 (일반적으로 생성자는 아니지만 반드시 **는 아닙니다)-자원을 획득하고 소멸자는 항상 자원을 해제합니다.
- 클래스의 로컬 인스턴스를 통해 리소스를 사용하십시오 *
- 개체가 범위를 벗어나면 리소스가 자동으로 해제됩니다.
이렇게하면 리소스를 사용하는 동안 발생하는 모든 일이 결국 반환됩니다 (정상적인 반환, 포함 된 객체의 파괴 또는 예외 발생으로 인해).
리소스를 처리하는 안전한 방법이 아니라 오류 처리 코드와 주요 기능을 혼합 할 필요가 없으므로 코드를 훨씬 깨끗하게 만들 수 있기 때문에 C ++에서 널리 사용되는 모범 사례입니다.
*
업데이트 : "local"은 로컬 변수 또는 클래스의 비 정적 멤버 변수를 의미 할 수 있습니다. 후자의 경우 멤버 변수는 소유자 객체로 초기화되고 소멸됩니다.
**
Update2 : @sbi가 지적했듯이 리소스는 종종 생성자 내부에 할당되지만 외부에 할당되어 매개 변수로 전달 될 수도 있습니다.
"RAII"는 "Resource Acquisition is Initialization (자원 획득은 초기화)"의 약자이며, 관련된 자원 획득 (및 객체의 초기화) 이 아니기 때문에 (오브젝트 의 파괴 를 통해) 자원을 방출 하기 때문에 실제로는 잘못된 이름 ). 그러나 RAII는 우리가 얻은 이름이며 그대로 유지됩니다.
바로이 관용구는 로컬 자동 개체에 리소스 (메모리 덩어리, 열린 파일, 잠금 해제 된 뮤텍스, you-name-it)를 캡슐화 하고 개체가 소멸 될 때 해당 개체의 소멸자가 리소스를 해제하도록하는 기능이 있습니다. 범위의 끝 :
{
raii obj(acquire_resource());
// ...
} // obj's dtor will call release_resource()
물론 개체가 항상 로컬 자동 개체 인 것은 아닙니다. 그들은 또한 수업의 일원이 될 수 있습니다.
class something {
private:
raii obj_; // will live and die with instances of the class
// ...
};
이러한 객체가 메모리를 관리하는 경우 종종 "스마트 포인터"라고합니다.
이것에는 많은 변형이 있습니다. 예를 들어, 첫 번째 코드 스 니펫에서 누군가 복사하려는 경우 어떻게 될지에 대한 의문이 생깁니다 obj
. 가장 쉬운 방법은 단순히 복사를 허용하지 않는 것입니다. std::unique_ptr<>
다음 C ++ 표준에 의해 특징 지어진 표준 라이브러리의 일부인 스마트 포인터가이를 수행합니다.
또 다른 스마트 포인터 std::shared_ptr
는 보유하고있는 자원 (동적으로 할당 된 객체)의 "공유 소유권"을 특징으로합니다. 즉, 자유롭게 복사 할 수 있으며 모든 사본은 동일한 객체를 참조합니다. 스마트 포인터는 동일한 객체를 참조하는 사본 수를 추적하고 마지막 사본이 삭제 될 때 삭제합니다.
세 번째 변형은std::auto_ptr
이것은 일종의 이동 의미론을 구현합니다. 객체는 하나의 포인터 만 소유하고 있으며 객체 복사를 시도하면 객체의 소유권을 복사 작업의 대상으로 이전합니다 (구문 해커를 통해).
디자인 패턴이 공개 된 C ++ 프로그래밍 서적 은 RAII를 다음과 같이 설명합니다.
- 모든 자원 확보
- 자원 사용
- 자원 해제
어디
리소스는 클래스로 구현되며 모든 포인터에는 주위에 클래스 래퍼가 있습니다 (스마트 포인터 만들기).
자원은 생성자를 호출하여 획득하고 소멸자를 호출하여 내재적으로 (취득 순서의 역순으로) 해제합니다.
개체의 수명은 범위에 따라 결정됩니다. 그러나 때로는 생성 된 범위와 독립적으로 존재하는 객체를 생성해야하거나 유용합니다. C ++에서 연산자 new
는 이러한 객체를 만드는 데 사용됩니다. 그리고 물체를 파괴하기 위해 연산자를 delete
사용할 수 있습니다. 운영자 new
가 생성 한 객체 는 동적으로 할당됩니다. 즉 동적 메모리 ( heap 또는 free store 라고도 함)에 할당됩니다 . 따라서에 의해 생성 된 객체 new
는를 사용하여 명시 적으로 파괴 될 때까지 계속 존재합니다 delete
.
사용할 때 발생할 수있는 몇 가지 실수 는 다음 new
과 delete
같습니다.
- 누출 된 객체 (또는 메모리) :
new
객체를 할당하고 객체를 잊어 버리는 데 사용delete
합니다. - 조기 삭제 (또는 매달려있는 참조 ) : 객체에
delete
대한 다른 포인터를 잡고 객체를 사용합니다. - 이중 삭제 :
delete
객체를 두 번 시도합니다 .
일반적으로 범위가 지정된 변수가 선호됩니다. 그러나 RAII가 대안으로 사용될 수 new
및 delete
라이브 독립적으로 범위의 목적을 위해. 이러한 기술은 힙에 할당 된 오브젝트에 대한 포인터를 핸들 / 관리자 오브젝트 에 배치하는 것으로 구성됩니다 . 후자는 물체 파괴를 담당하는 소멸자를 가지고 있습니다. 이렇게하면 객체를 액세스하려는 모든 함수에서 객체를 사용할 수 있으며 핸들 객체 의 수명이 종료되면 명시 적으로 정리할 필요없이 객체가 파괴됩니다 .
RAII를 사용하는 C ++ 표준 라이브러리의 예는 std::string
and std::vector
입니다.
이 코드 조각을 고려하십시오.
void fn(const std::string& str)
{
std::vector<char> vec;
for (auto c : str)
vec.push_back(c);
// do something
}
벡터를 만들고 요소를 푸시 할 때 이러한 요소를 할당하고 할당 해제하는 데 신경 쓰지 않습니다. 벡터는 new
힙에 요소의 공간을 할당하고 해당 공간을 비우는 데 사용 됩니다 delete
. 벡터 사용자는 구현 세부 사항에 신경 쓰지 않고 벡터가 누출되지 않도록 신뢰합니다. 이 경우 벡터는 해당 요소 의 핸들 객체 입니다.
표준 라이브러리에서 다른 예로는 사용 RAII는 것을 std::shared_ptr
, std::unique_ptr
하고 std::lock_guard
.
이 기술의 다른 이름은 SBRM 으로 Scope-Bound Resource Management의 줄임말입니다 .
수동 메모리 관리는 프로그래머가 컴파일러를 발명 한 이후로 피할 수있는 방법을 고안해 낸 악몽입니다. 가비지 수집기를 사용하여 프로그래밍 언어를 사용하면 작업이 쉬워 지지만 성능이 저하됩니다. 이 기사- 가비지 컬렉터 제거 : RAII Way , Toptal 엔지니어 인 Peter Goodspeed-Niklaus는 가비지 컬렉터의 역사를 엿보고 소유권과 차용 개념이 안전 보장을 손상시키지 않으면 서 가비지 컬렉터를 제거하는 데 어떻게 도움이되는지 설명합니다.
RAII 클래스에는 세 부분이 있습니다.
- 자원은 소멸자에게 양도됩니다
- 클래스의 인스턴스는 스택 할당됩니다
- 생성자에서 리소스를 얻습니다. 이 부분은 선택 사항이지만 일반적입니다.
RAII는 "자원 획득은 초기화입니다."를 나타냅니다. RAII의 "자원 획득"부분은 다음과 같이 나중에 종료해야하는 것을 시작하는 곳입니다.
- 파일 열기
- 메모리 할당
- 잠금 획득
"초기화"부분은 클래스의 생성자 안에서 획득이 발생 함을 의미합니다.
'Programing' 카테고리의 다른 글
Vim : 커서를 마지막 위치로 이동 (0) | 2020.04.09 |
---|---|
별도의 리모콘을 추가하지 않고 어떻게 포크를 동기화 상태로 유지할 수 있습니까? (0) | 2020.04.09 |
C ++에서 CSV 파일을 읽고 구문 분석하는 방법 (0) | 2020.04.09 |
“sh”또는“bash”명령을 사용하지 않고 쉘 스크립트를 어떻게 실행합니까? (0) | 2020.04.09 |
기존 파일을 열고 한 줄 추가 (0) | 2020.04.09 |