레슨 6 / 8·20분
스마트 포인터와 메모리 관리
C++의 메모리 관리 문제
C 스타일의 new/delete 수동 메모리 관리는 메모리 누수, 이중 해제, 댕글링 포인터 등 다양한 버그의 원인이 됩니다. C++11부터 도입된 스마트 포인터는 RAII(Resource Acquisition Is Initialization) 패턴으로 이 문제를 해결합니다.
unique_ptr — 독점 소유권
cpp
#include <iostream>
#include <memory>
using namespace std;
class Player {
public:
string name;
int hp;
Player(const string& n, int h) : name(n), hp(h) {
cout << name << " 생성" << endl;
}
~Player() {
cout << name << " 소멸" << endl;
}
void info() {
cout << name << " (HP: " << hp << ")" << endl;
}
};
int main() {
// unique_ptr: 하나의 포인터만 객체를 소유
auto player1 = make_unique<Player>("전사", 100);
player1->info(); // 전사 (HP: 100)
// 소유권 이전 (move)
unique_ptr<Player> player2 = move(player1);
// player1은 이제 nullptr
if (!player1) {
cout << "player1은 비어있음" << endl;
}
player2->info(); // 전사 (HP: 100)
// 배열도 가능
auto scores = make_unique<int[]>(5);
for (int i = 0; i < 5; i++) {
scores[i] = (i + 1) * 10;
}
// 스코프 끝나면 자동 해제!
return 0;
}shared_ptr과 weak_ptr
cpp
#include <iostream>
#include <memory>
using namespace std;
class Node {
public:
string value;
shared_ptr<Node> next;
weak_ptr<Node> parent; // 순환 참조 방지
Node(const string& v) : value(v) {
cout << value << " 생성" << endl;
}
~Node() {
cout << value << " 소멸" << endl;
}
};
int main() {
// shared_ptr: 여러 포인터가 객체를 공유
auto node1 = make_shared<Node>("A");
cout << "참조 카운트: " << node1.use_count() << endl; // 1
{
auto node2 = node1; // 공유
cout << "참조 카운트: " << node1.use_count() << endl; // 2
} // node2 소멸, 카운트 감소
cout << "참조 카운트: " << node1.use_count() << endl; // 1
// weak_ptr: 소유권 없이 참조
weak_ptr<Node> weakRef = node1;
if (auto locked = weakRef.lock()) {
cout << "유효: " << locked->value << endl;
}
return 0;
}RAII와 이동 의미론
cpp
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Buffer {
int* data;
size_t size;
public:
// 생성자
Buffer(size_t n) : size(n), data(new int[n]) {
cout << "할당: " << n << " ints" << endl;
}
// 소멸자 (RAII: 자동 정리)
~Buffer() {
delete[] data;
cout << "해제" << endl;
}
// 이동 생성자 (복사 대신 소유권 이전)
Buffer(Buffer&& other) noexcept
: data(other.data), size(other.size) {
other.data = nullptr;
other.size = 0;
cout << "이동 완료" << endl;
}
// 복사 금지
Buffer(const Buffer&) = delete;
Buffer& operator=(const Buffer&) = delete;
size_t getSize() const { return size; }
};
int main() {
Buffer buf1(100);
Buffer buf2 = move(buf1); // 이동 (복사 아님)
cout << "buf2 크기: " << buf2.getSize() << endl;
// vector에서의 이동
vector<string> v;
string s = "Hello, World!";
v.push_back(move(s)); // 이동 (복사 비용 없음)
// s는 이제 빈 문자열
return 0;
}- •
unique_ptr— 독점 소유, 복사 불가, 이동만 가능 - •
shared_ptr— 공유 소유, 참조 카운팅으로 자동 해제 - •
weak_ptr— 소유권 없는 참조, 순환 참조 방지 - •
make_unique/() make_shared— 안전한 생성() - •
move()— 소유권 이전, 불필요한 복사 제거 - •RAII — 리소스 획득 = 초기화, 소멸자에서 자동 정리
💡
Modern C++에서는 new/delete를 직접 사용하지 마세요. 항상 make_unique 또는 make_shared를 사용하세요. 순환 참조가 예상되면 한쪽을 weak_ptr로 선언합니다.