개체 수명 및 리소스 관리(RAII)

관리되는 언어와 달리 C++에는 프로그램이 실행될 때 힙 메모리 및 기타 리소스를 해제하는 내부 프로세스인 자동 가비지 수집이 없습니다. C++ 프로그램은 획득한 모든 리소스를 운영 체제로 반환하는 역할을 담당합니다. 사용하지 않는 리소스를 해제하지 못한 경우 누수라고 합니다. 프로세스가 종료될 때까지 유출된 리소스는 다른 프로그램에서 사용할 수 없습니다. 특히 메모리 누수는 C 스타일 프로그래밍에서 버그의 일반적인 원인입니다.

최신 C++는 스택에 개체를 선언하여 힙 메모리를 최대한 사용하지 않도록 방지합니다. 리소스가 스택에 비해 너무 크면 개체가 소유해야 합니다. 개체가 초기화되면 소유하는 리소스를 획득합니다. 그런 다음, 개체는 소멸자에서 리소스를 해제합니다. 소유 개체 자체는 스택에 선언됩니다. 개체가 리소스를 소유한다는 원칙을 "리소스 획득은 초기화입니다" 또는 RAII라고도 합니다.

리소스 소유 스택 개체가 범위를 벗어나면 소멸자가 자동으로 호출됩니다. 이러한 방식으로 C++의 가비지 수집은 개체 수명과 밀접한 관련이 있으며 결정적입니다. 리소스는 항상 제어할 수 있는 프로그램의 알려진 지점에서 해제됩니다. C++와 같은 결정적 소멸자만 메모리 및 비 메모리 리소스를 동일하게 처리할 수 있습니다.

다음 예제에서는 간단한 개체 w를 보여줍니다. 함수 범위의 스택에 선언되고 함수 블록의 끝에서 제거됩니다. 개체 w 는 리소스(예: 힙 할당 메모리)를 소유하지 않습니다. 유일한 멤버 g 는 자체적으로 스택에 선언되며, 단순히 함께 w범위를 벗어나갑니다. 소멸자에서 특별한 코드가 widget 필요하지 않습니다.

class widget {
private:
    gadget g;   // lifetime automatically tied to enclosing object
public:
    void draw();
};

void functionUsingWidget () {
    widget w;   // lifetime automatically tied to enclosing scope
                // constructs w, including the w.g gadget member
    // ...
    w.draw();
    // ...
} // automatic destruction and deallocation for w and w.g
  // automatic exception safety,
  // as if "finally { w.dispose(); w.g.dispose(); }"

다음 예제 w 에서는 메모리 리소스를 소유하므로 해당 소멸자의 코드가 있어야 메모리를 삭제할 수 있습니다.

class widget
{
private:
    int* data;
public:
    widget(const int size) { data = new int[size]; } // acquire
    ~widget() { delete[] data; } // release
    void do_something() {}
};

void functionUsingWidget() {
    widget w(1000000);  // lifetime automatically tied to enclosing scope
                        // constructs w, including the w.data member
    w.do_something();

} // automatic destruction and deallocation for w and w.data

C++11부터 표준 라이브러리의 스마트 포인터를 사용하여 이전 예제를 작성하는 더 좋은 방법이 있습니다. 스마트 포인터는 소유하는 메모리의 할당 및 삭제를 처리합니다. 스마트 포인터를 사용하면 클래스에서 명시적 소멸자가 widget 필요하지 않습니다.

#include <memory>
class widget
{
private:
    std::unique_ptr<int[]> data;
public:
    widget(const int size) { data = std::make_unique<int[]>(size); }
    void do_something() {}
};

void functionUsingWidget() {
    widget w(1000000);  // lifetime automatically tied to enclosing scope
                        // constructs w, including the w.data gadget member
    // ...
    w.do_something();
    // ...
} // automatic destruction and deallocation for w and w.data

메모리 할당에 스마트 포인터를 사용하면 메모리 누수 가능성이 제거될 수 있습니다. 이 모델은 파일 핸들 또는 소켓과 같은 다른 리소스에 대해 작동합니다. 클래스에서 비슷한 방식으로 고유한 리소스를 관리할 수 있습니다. 자세한 내용은 스마트 포인터를 참조 하세요.

C++의 디자인은 개체가 범위를 벗어날 때 제거되도록 합니다. 즉, 블록이 종료되면 역순으로 생성됩니다. 개체가 제거되면 해당 베이스와 멤버가 특정 순서로 제거됩니다. 전역 범위에서 블록 외부에서 선언된 개체는 문제가 발생할 수 있습니다. 전역 개체의 생성자가 예외를 throw하는 경우 디버그하기 어려울 수 있습니다.

참고 항목

C++ 시작하기
C++ 언어 참조
C++ 표준 라이브러리