
C++에서 메모리와 객체를 보다 깊이 이해하려면 반드시 포인터와 레퍼런스를 알아야 한다. 이 글에서는 C++ 개발자 로드맵에 따라 포인터, 레퍼런스, 메모리 모델, 객체 수명, 그리고 스마트 포인터까지 폭넓게 정리해보았다.
포인터는 다른 변수의 메모리 주소를 저장하는 변수다. 변수 자체가 아닌, 메모리 위치를 가리킨다.
int num = 10;
int* ptr = # // ptr은 num의 주소를 저장
int value = *ptr; // value는 10
int add(int a, int b) {
return a + b;
}
int main() {
int (*funcptr)(int, int) = add;
std::cout << funcptr(4, 5); // 9
}
레퍼런스는 기존 변수의 또 다른 이름(별칭)이다. 선언 시 반드시 초기화해야 하고, 이후 다른 대상을 참조할 수 없다.
int num = 10;
int& ref = num;
ref = 30;
std::cout << num; // 30
void swap(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
C++의 메모리 구조는 다음 네 가지 주요 영역으로 나뉜다.
void func() {
int x = 10; // 스택에 저장
}
int* p = new int(5);
// ...
delete p;
int globalVar = 10; // 초기화된 데이터
static int staticVar; // 비초기화(BSS)
C++ 객체는 다음과 같은 수명 범주를 가진다:
static int count;
int globalVar;
thread_local int counter;
void func() {
int localVar;
}
int* ptr = new int(100);
delete ptr;
std::unique_ptr<int> uptr = std::make_unique<int>(10);
std::unique_ptr<int> uptr2 = std::move(uptr); // 이동
std::shared_ptr<int> sp1 = std::make_shared<int>(42);
std::shared_ptr<int> sp2 = sp1;
std::cout << sp1.use_count(); // 2
std::weak_ptr<int> wp = sp1;
if (auto spt = wp.lock()) {
std::cout << *spt;
}
int* num = new int(10);
delete num;
int* arr = new int[10];
delete[] arr;
void leak() {
int* ptr = new int(100);
// delete 누락 → 누수 발생
}
C++의 강력함은 포인터와 메모리 제어에서 시작된다. 하지만 그만큼 위험성도 함께 따른다. 스마트 포인터를 잘 활용하고 객체의 수명과 메모리 구조를 이해한다면 더욱 안전하고 효율적인 C++ 코드를 작성할 수 있다.