[Design Pattern] RAII(Resource Acquisition Is Initialization)

최지수·2022년 7월 5일
0

Design Pattern

목록 보기
1/1
post-thumbnail

RAII

C++ 개발자로 지낸지 3년이 넘어갈 즈음에 알게된 패턴이네요반성합네다.... RAII자원의 안전한 사용을 위해 객체가 쓰이는 스코프를 벗어나면 자원을 해제해주는 기법이에요. C++ 고유의 디자인 패턴이라고 하는데 그 이유는 초기화된 인스턴스가 스코프를 벗어나면 소멸자를 호출시켜 자원을 해제하는 C++에서만 구사할 수 있는 방식이에요. C++11에서 사용할 수 있는 스마트 포인터와 함께 자주 언급이 되요.

리소스 획득

변수에 접근 가능한 시점에 변수 사용이 가능하다는 점을 컴파일 단계에서 정할 수 있어요. 아래 코드를 보며 이해해봐요.

class foo {
public:
    init () {
        /* 초기화 */
    }
    dispose() {
        /* 정리 */
    }
    
    doAction() {
        /* 뭔가 함 */
    }
};

근데 위 코드는 문제가 많아요. 본격적으로 작성해봐요.

class foo {
public:
    ~foo() {
        dispose();
    }

    init () {
        if (이전에 초기화 했나?) return;
        /* 초기화 */
    }
    dispose() {
        if (아직 초기화조차 안되었나?) return;
        if (이미 dispose 되었나?) return;
        /* 정리 */
    }
    
    doAction1() {
        if (이전에 초기화 안했나?) return;
        /* 뭔가 함 */
    }
    doAction2() {
        if (이전에 초기화 안했나?) return;
        /* 뭔가 함 */
    }
    doAction3() {
        if (이전에 초기화 안했나?) return;
        /* 뭔가 함 */
    }
};

근데 여기서 init 가 실패한 경우엔 dispose를 해야할까요? 아니면 init이 안됬으니까 굳이 dispose를 호출할 필요도 없을까요?

RAII는 단순히 생성자와 파괴자를 호출하는 문법이 아니라, 이에 대한 해답을 제공해주는 철학적인 접근을 가졌어요.

  • 접근 가능 시점에 이미 리소스가 초기화되었음을 보장해요ㅋ
  • 초기화가 실패하면 throw되어 스코프 밖으로 넘어감으로 자동으로 파괴자가 호출되요
  • 스코프 밖으로 나갔으니 disposedoAction 이 불려질 일이 없습니다. doAction은 항상 초기화가 되어있음을 가정하고 동작할 수 있어요

파괴자 호출

초기화 뿐만 아니라 파괴에 대한 솔루션도 제공해요. 저는 이 부분이 참 마음에 들어요. 이를 활용해 만든 특정 메서드의 퍼포먼스를 측정하는 클래스를 만들었어요.

#pragma once
#include <iostream>
#include <chrono>
#include <limits>

class TimeChecker final
{
public:
    TimeChecker(std::string processName)
    {
        this->start = std::chrono::system_clock::now();
        this->processName = processName;
    }

    ~TimeChecker()
    {
        std::chrono::duration<double> sec = std::chrono::system_clock::now() - start;
        std::cout.precision(3);
        std::cout << processName << " : " << sec.count() << " sec(s)" << std::endl;
    }

private:
    std::string processName;
    std::chrono::system_clock::time_point start;
};

해당 인스턴스를 지역변수로 선언하면 메서드를 벗어날 때 자동으로 측정 결과를 반환해요.

{
	TimeChecker checker;
    
    // 로지이ㅣ이이익
}

측정을 위해 아래처럼 번거로운 선언이 필요없죠.

{
    std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
    
    // 로직을 수행해요.
	// 
	
    std::chrono::duration<double> sec = std::chrono::system_clock::now() - start;
    std::cout.precision(3);
    std::cout << processName << " : " << sec.count() << " sec(s)" << std::endl;
}

개발하는 과정에서 퍼포먼스 측정이 필요한 경우가 많았어요. 그 때마다 now()니, 측정 시작 변수 초기화니 등등 작업하는 번거로움이 있어 이를 편하게 하는 방법이 없을 까 고민을 많이 했었는데 이번 공부를 통해 해소해서 기분이 좋았어요 ㅎㅎ. 당연하게 쓰던 것에서 답을 찾는다. 좋은 것을 배웠습니다.

RAII에 대한 내용은 여기까지 다룰게요.

참고

pjc0247.log - 영감을 주는 코딩 패턴들 - RAII
슭의 개발 블로그 - RAII는 무엇인가

profile
#행복 #도전 #지속성

0개의 댓글