C++ Lifetime | "객체 수명" 가이드
이 글의 핵심
C++ Lifetime에 대한 실전 가이드입니다. 개념부터 실무 활용까지 예제와 함께 상세히 설명합니다.
Lifetime이란?
객체가 생성부터 소멸까지 존재하는 기간
void func() {
int x = 10; // x 생성
// x의 수명: 여기서 사용 가능
} // x 소멸
저장 기간 (Storage Duration)
// 1. 자동 저장 기간 (Automatic)
void func() {
int x = 10; // 함수 종료 시 소멸
}
// 2. 정적 저장 기간 (Static)
int global = 10; // 프로그램 종료 시 소멸
void func() {
static int count = 0; // 프로그램 종료 시 소멸
}
// 3. 동적 저장 기간 (Dynamic)
void func() {
int* ptr = new int(10); // 명시적 delete 필요
delete ptr;
}
// 4. 스레드 저장 기간 (Thread)
thread_local int x = 10; // 스레드 종료 시 소멸
실전 예시
예시 1: 자동 저장 기간
#include <iostream>
class Widget {
public:
Widget(int id) : id(id) {
std::cout << "Widget " << id << " 생성" << std::endl;
}
~Widget() {
std::cout << "Widget " << id << " 소멸" << std::endl;
}
private:
int id;
};
void func() {
Widget w1(1);
if (true) {
Widget w2(2);
// w2 수명: 이 블록 내
} // w2 소멸
Widget w3(3);
} // w3, w1 소멸 (역순)
int main() {
func();
}
예시 2: 정적 저장 기간
#include <iostream>
class Logger {
public:
Logger() {
std::cout << "Logger 초기화" << std::endl;
}
~Logger() {
std::cout << "Logger 종료" << std::endl;
}
void log(const std::string& msg) {
std::cout << "[LOG] " << msg << std::endl;
}
};
// 전역 객체
Logger globalLogger;
void func() {
// 지역 정적 객체
static Logger localLogger;
localLogger.log("함수 호출");
}
int main() {
globalLogger.log("프로그램 시작");
func();
func();
globalLogger.log("프로그램 종료");
}
예시 3: 동적 저장 기간
#include <memory>
class Resource {
public:
Resource(int size) : size(size) {
data = new int[size];
std::cout << "Resource 할당: " << size << std::endl;
}
~Resource() {
delete[] data;
std::cout << "Resource 해제: " << size << std::endl;
}
private:
int* data;
int size;
};
void manualManagement() {
Resource* res = new Resource(100);
// 사용
delete res; // 명시적 해제
}
void smartPointer() {
auto res = std::make_unique<Resource>(100);
// 자동 해제
}
int main() {
manualManagement();
smartPointer();
}
예시 4: 수명 연장
#include <iostream>
class Temp {
public:
Temp(int val) : value(val) {
std::cout << "Temp 생성: " << value << std::endl;
}
~Temp() {
std::cout << "Temp 소멸: " << value << std::endl;
}
int getValue() const {
return value;
}
private:
int value;
};
Temp createTemp(int val) {
return Temp(val);
}
int main() {
// 임시 객체 수명 연장
const Temp& ref = createTemp(10);
std::cout << "값: " << ref.getValue() << std::endl;
// ref가 스코프 벗어날 때 소멸
}
생성/소멸 순서
#include <iostream>
class A {
public:
A(int id) : id(id) {
std::cout << "A" << id << " 생성" << std::endl;
}
~A() {
std::cout << "A" << id << " 소멸" << std::endl;
}
private:
int id;
};
A global1(1); // 전역 객체 (먼저 생성)
int main() {
A local1(2);
static A static1(3);
A local2(4);
// 소멸 순서: local2 -> local1 -> static1 -> global1
}
자주 발생하는 문제
문제 1: 댕글링 레퍼런스
// ❌ 댕글링 레퍼런스
const std::string& func() {
std::string s = "Hello";
return s; // s는 함수 종료 시 소멸
}
int main() {
const std::string& ref = func();
// ref는 소멸된 객체 참조 (위험!)
}
// ✅ 값 반환
std::string func() {
std::string s = "Hello";
return s; // 복사 또는 이동
}
문제 2: 댕글링 포인터
// ❌ 댕글링 포인터
int* func() {
int x = 10;
return &x; // x는 함수 종료 시 소멸
}
int main() {
int* ptr = func();
// *ptr은 위험!
}
// ✅ 동적 할당 또는 값 반환
int* func() {
return new int(10); // 동적 할당
}
int func() {
return 10; // 값 반환
}
문제 3: 정적 초기화 순서
// file1.cpp
int globalA = 10;
// file2.cpp
extern int globalA;
int globalB = globalA * 2; // 초기화 순서 불확실
// ✅ 함수 내 정적 변수
int& getGlobalA() {
static int globalA = 10;
return globalA;
}
int globalB = getGlobalA() * 2; // 안전
문제 4: 임시 객체 수명
// ❌ 임시 객체 즉시 소멸
std::string getName() {
return "Alice";
}
const char* ptr = getName().c_str(); // 위험!
// getName()의 임시 객체 소멸
// ptr은 댕글링
// ✅ 수명 연장
const std::string& name = getName();
const char* ptr = name.c_str(); // 안전
스마트 포인터와 수명
#include <memory>
class Resource {
public:
Resource() {
std::cout << "Resource 생성" << std::endl;
}
~Resource() {
std::cout << "Resource 소멸" << std::endl;
}
};
void uniquePtr() {
auto res = std::make_unique<Resource>();
// 스코프 벗어나면 자동 소멸
}
void sharedPtr() {
auto res1 = std::make_shared<Resource>();
{
auto res2 = res1; // 참조 카운트 증가
// res2 스코프 벗어남
}
// res1 스코프 벗어나면 소멸
}
int main() {
uniquePtr();
sharedPtr();
}
RAII 패턴
#include <fstream>
#include <mutex>
// 파일 자동 관리
void writeFile(const std::string& filename) {
std::ofstream file(filename); // 생성 시 열림
file << "Hello";
// 소멸 시 자동 닫힘
}
// 뮤텍스 자동 관리
std::mutex mtx;
void criticalSection() {
std::lock_guard<std::mutex> lock(mtx); // 생성 시 잠금
// 임계 영역
// 소멸 시 자동 해제
}
FAQ
Q1: Lifetime은 언제 시작?
A:
- 생성자 완료 후
- 초기화 완료 후
Q2: 언제 종료?
A:
- 소멸자 호출 시
- 스코프 벗어남
- delete 호출
Q3: 저장 기간 종류는?
A:
- 자동 (지역 변수)
- 정적 (전역, static)
- 동적 (new/delete)
- 스레드 (thread_local)
Q4: 댕글링 방지는?
A:
- 스마트 포인터
- 값 반환
- RAII 패턴
Q5: 수명 연장은?
A: const 레퍼런스로 임시 객체 바인딩.
Q6: Lifetime 학습 리소스는?
A:
- “Effective C++”
- “C++ Primer”
- cppreference.com
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++ Dangling Reference | “댕글링 레퍼런스” 가이드
- C++ Use After Free | “해제 후 사용” 가이드
- C++ Memory Leak | “메모리 누수” 가이드
관련 글
- C++ Buffer Overflow |
- C++ Cache Optimization |
- C++ Custom Allocator |
- C++ Dangling Reference |
- C++ Flyweight 패턴 완벽 가이드 | 공유로 메모리 절약하기