Computer >> 컴퓨터 >  >> 프로그래밍 >> C++

C++ RAII와 스마트 포인터: 자원 관리의 핵심 기법

C++에서의 RAII란?

RAII(Resource Acquisition Is Initialization, 자원 획득은 곧 초기화)는 C++에서 자원의 생명 주기를 객체의 생명 주기에 연결해 관리하는 핵심 프로그래밍 기법입니다. 이 기법은 클래스 기반으로 구현되며, 자원의 획득과 해제를 컴파일러가 자동으로 처리하도록 만들어 줍니다.

RAII는 여러 자원을 하나의 클래스로 캡슐화합니다. 객체가 생성될 때 생성자가 자원 할당을 담당하고, 객체가 소멸될 때 소멸자가 자원 해제를 수행합니다.

이 덕분에 예외가 발생하거나 함수가 중간에 반환되더라도 스택 되감기(stack unwinding) 과정에서 소멸자가 반드시 호출되므로 자원 누수(resource leak)를 방지할 수 있습니다. 즉, 객체가 살아 있는 동안에는 해당 자원이 반드시 유지된다는 보장을 받게 됩니다.

RAII 활용 예제

void file_write() {
    static mutex m;                    // 파일 접근을 보호하는 뮤텍스
    lock_guard<mutex> lock(m);         // 파일 접근 전 뮤텍스 잠금
    ofstream file("a.txt");
    if (!file.is_open()) {             // 파일이 열리지 않은 경우
        throw runtime_error("unable to open file");
    }
    // 파일에 텍스트 쓰기
    file << text << std::endl;
}

위 예제에서 lock_guard가 대표적인 RAII 활용 사례입니다. 함수가 어떤 경로로 종료되더라도 lock_guard 객체의 소멸자가 호출되어 뮤텍스가 자동으로 해제되므로, 데드락 위험 없이 안전하게 동기화할 수 있습니다.

C++ 스마트 포인터란?

스마트 포인터(Smart Pointer)는 일반 포인터를 추상화한 데이터 타입으로, 파일 처리나 네트워크 소켓 같은 자원을 메모리처럼 관리할 수 있게 해줍니다. 자동 소멸, 참조 카운팅 등 다양한 기능을 제공하여 수동 메모리 관리로 인한 실수를 크게 줄여줍니다.

C++에서 스마트 포인터는 템플릿 클래스로 구현되며, 역참조 연산자(*)와 화살표 연산자(->)를 오버로딩하여 일반 포인터처럼 사용할 수 있습니다. 대표적인 스마트 포인터로는 auto_ptr, shared_ptr, unique_ptr, weak_ptr이 있으며, 참고로 auto_ptr은 C++11부터 폐기(deprecated)되었고 C++17에서 완전히 제거되었으므로 현재는 unique_ptr 사용이 권장됩니다.

스마트 포인터 구현 예제

#include <iostream>
using namespace std;

// 범용 스마트 포인터 클래스
template <class T>
class Smartpointer {
    T *p; // 실제 포인터
public:
    // 생성자
    Smartpointer(T *ptr = NULL) {
        p = ptr;
    }
    // 소멸자
    ~Smartpointer() {
        delete(p);
    }
    // 역참조 연산자 오버로딩
    T & operator * () {
        return *p;
    }
    // 화살표 연산자 오버로딩 (T의 멤버를 포인터처럼 접근 가능)
    T * operator -> () {
        return p;
    }
};

int main() {
    Smartpointer<int> p(new int());
    *p = 26;
    cout << "Value is: " << *p;
    return 0;
}

실행 결과

Value is: 26

이 예제에서 Smartpointer 객체는 main 함수가 끝날 때 자동으로 소멸되며, 소멸자가 내부적으로 delete를 호출해 동적 메모리를 해제합니다. 이처럼 스마트 포인터를 사용하면 delete 호출을 잊어 발생하는 메모리 누수를 원천적으로 방지할 수 있습니다.