C++에서 Object Lifetime은 객체가 생성되고, 사용되고, 소멸되는 기간을 총체적으로 부르는 명칭이다. Lifetime은 C++의 메모리 모델과도 밀접하게 연관이 되어있고, 어느 메모리 영역에 할당되어있는지에 따라서도 구분지을 수 있다. 이번 포스팅에서는 Object Lifetime에 대해서 간단하게 다뤄볼 생각이다.
이 lifetime을 가지는 객체는 스택 메모리 에 하랃외며, 주로 함수 내에서 선언된 지역 변수(local variable)가 해당된다. 이러한 객체(지역 변수)는 선언된 코드 블록(code block, 함수, 조건문, 반복문 등)을 벗어나게 되면 자동으로 소멸되어 메모리 영역에서 제거된다.
void function(){
int localVar = 10;
// 지역 변수는 스택 메모리에 할당된다.
// localVar 변수는 function 블록 내에서만 lifetime이 유효하며
// 함수가 끝나면 자동으로 소멸한다.
}
이 lifetime을 가지는 객체는 힙 메모리 에 할당되며, new와 같은 동적 메모리 할당 연산자를 통해서 생성된다. 이러한 객체는 개발자가 delete 연산자를 통해 명시적으로 메모리를 해제할 때까지 메모리에서 남아읶데 된다.
int main(){
int* ptr = new int(20); // 힙 메모리 할당
// ptr이 가리키는 객체 생명 주기는 delete가 호출될 때까지 유효하다
delete ptr; // 힙 메모리 해제, 객체 소멸
return 0;
이 lifetime을 가지는 객체들은 데이터 세그먼트 에 할당되며, 프로그램이 시작될 때 생성되고, 프로그램이 종료될 때까지 유지된다. 데이터 세그먼트 영역에는 전역 변수(global variable)와 'static' 키워드로 선언된 변수들이 들어간다.
int globalVar = 100; // 데이터 세그먼트에 메모리 할당
void function(){
static int staticVar = 200; // 데이터 세그먼트에 할당
// 초기화는 딱 1번만 이루어진다.
staticVar++; // 호출될 때마다 값이 1씩 증가한다.
cout << staticVar << endl;
}
int main(){
function();
function();
return 0;
}
이 lifetime을 가지는 객체는, 특정 스레드(Thread) 내에서만 유효한 생명 주기를 가진다. thread_local 키워를 사용하여 선언된 변수가 해당 영역에 할당되며, 스레드가 종료될 때 소멸된다.
#include <iostream>
#incldue <thread>
using namespace std;
thread_local int localVar = 0; // 스레드마다 독립적인 저장 공간
void threadFunction(){
localVar++;
cout << localVar << endl;
}
int main(){
thread t1(threadFunction);
threade t2(threadFunction);
t1.join();
t2.join();
}
C++에서는 RAII 패턴을 통해서 객체의 Lifetime을 관리할 수 있다. 이 패턴은 자원의 획득(메모리 할당, 파일 불러와 열기 등)을 객체 생성자에서 처리하고, 자원의 해제(메모리 해제, 파일 닫기 등)를 소멸자에서 처리함으로써 자원 누수 문제를 방지하게 된다.
예시를 보면 다음과 같다.
#include<iostream>
using namespace std;
class Resource {
public:
Resource() {
// 자원 획득 (예: 파일 열기, 메모리 할당 등)
cout << "자원 획득" << endl;
}
~Resource() {
// 자원 해제 (예: 파일 닫기, 메모리 해제 등)
cout << "자원 해제" << endl;
}
};
int main() {
{
Resource res; // res 객체가 생성될 때 자원을 획득한다.
} // 이 스코프를 벗어나면 소멸되며 자동으로 자원이 해제된다.
}