RAII
RAII는 Resoucre Acquisition Is Initialization(리소스 획득=초기화)의 줄임말이다.
RAII는 간단하지만 상당히 막강한 개념으로서, RAII 인스턴스가 스코프를 벗어나면 할당했던 리소스를 자동으로 해제하는 데 활용된다. 이렇게 동작하는 시점은 미리 알 수 있다. 기본적으로 새로운 RAII 인스턴스의 생성자가 특정한 리소스에 대한 소유권을 획득해서 그 리소스에 대한 인스턴스를 초기화 하는 것이다. 그래서 Resoucre Acquisition Is Initialization 라고 부른다. RAII 인스턴스가 사라지면 소멸자는 획득했던 리소스를 자동으로 해제한다.
핵심: Heap에 할당된 자원은 명시적으로 해제하지 않으면 해제가 안되지만 지역변수의 경우 스코프를 벗어나면 소멸자(Destructor)가 호출되는 것을 이용한 것이다.
RAII가 필요한 이유
다음과 같은 경우를 보자 이 경우는 어느정도 C++을 해본 사람이라면 메무리 누수(memory Leak)이 발생한다는 것을 알 수 있다.
그러면 Function() 밑에 delete 연산자를 추가해서 진행해보자
이러면 정상 종료된다고 생각할 수 있다. 하지만 기대와 다르게 메모리 누수의 가능성은 아직도 남아있다.
그 이유는 Functino()함수에서 예외(Exception)가 발생하면 delete 가 실행되지 않기 때문이다.
RAII File
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
class File {
private:
std::FILE* file_;
public:
//복사 생성자와, 복사 대입 연산자를 허용하지 않는다
File(const File&) = delete;
File& operator=(const File&) = delete;
// 이동 생성과, 이동 대입 연산자는 허용한다.
File( File&&) = default;
File& operator=( File&&) = default;
File(std::FILE* file):file_(file) {
}
~File() {
std::cout << "Call Destructor Function\n";
Reset();
}
//Get(), Release(), reset()
std::FILE* Get()const noexcept {
std::cout << "Call Get Function\n";
return file_;
}
std::FILE* Release() noexcept {
std::cout << "Call Release Function\n";
std::FILE* file = file_;
file_ = nullptr;
return file;
}
void Reset(std::FILE* file = nullptr)noexcept {
std::cout << "Call Reset Function\n";
if (file_) {
delete file_;
}
file_ = file;
}
};
int main() {
File myfile(fopen("Temp.txt", "r"));
}
|
RAII Mutex
RAII 클래스를 정의할 때 디폴트 생성자를 추가하거나 디폴트 생성자를 명시적으로 delete 하지 않는 것이 좋다. 왜 그런지는 표준 라이브러리의 RAII클래스인 std::unique_lock()을 직접 사용해보면 알 수 있다.
해당 함수는 RAII인 unique_lock의 객체를 이용하여 데이터 멤버에 락을 걸었다가 메서드가 끝날 즈음 자동으로 락을 해제하는 로컬 lock 객체를 생성한다. 그런데 이 lock 객체를 정의한 뒤 직접 사용하지 않기 때문에 다음과 같은 실수를 저지르기 쉽다.
이렇게 해도 컴파일은 되지만 의도와 다르게 실행된다. 이렇게하면 lock_ 로컬 변수를 선언하고 unique_lock의 디폴트생성자를 호출해서 이 변수를 초기화한다. 따라서 데이터 멤버인 lock_에 대해 락이 걸리지 않는다.
핵심: RAII 클래스에 절대로 디폴트 생성자를 두지 않는다.
후기
RAII에 대해서 책에 있는 내용을 간단하게 정리 하였다. C++은 따로 가비지 컬렉터가 없기 때문에 메모리를 할당하고 지우는거에 실수가 발생하기 마련이다. 이러한 것을 방지하기 위해 스마트 포인터 등 다양한 포인터가 최신 C++에서 지원되고 있고 이러한 스마트포인터는 RAII기반으로 구현이 된다는 것을 알 수 있었다.
출처 및 레퍼런스
Book: 전문가를 위한 C++ 17
'프로그래밍 > Modern C++' 카테고리의 다른 글
[C++] 멤버 함수를 Thread에 등록하는 방법 (0) | 2020.07.14 |
---|---|
[C++] private 함수보다는 delete 를 사용하자 (0) | 2020.06.28 |
[C++] Vector의 메모리 할당 방식 (0) | 2020.06.18 |
[C++] 2차원 Vector의 초기화 (0) | 2020.04.13 |
[C++] 용어 정리(1) 객체지향 철학, 객체지향 디자인 원칙(SOLID) (0) | 2020.03.22 |