2025. 2. 12. 18:56ㆍ프로그래밍 언어/C++
📌 5.4 스마트 포인터 (std::unique_ptr, std::shared_ptr, std::weak_ptr)
C++11부터 스마트 포인터(Smart Pointer)가 도입되어,
개발자가 직접 delete를 호출하지 않아도 메모리가 자동 관리된다.
스마트 포인터를 사용하면 메모리 누수를 방지하고, 포인터의 안전성을 향상시킬 수 있다.
C++ 표준 라이브러리 <memory> 헤더를 포함하면 사용할 수 있다.
📌 1. 스마트 포인터의 필요성
일반 포인터(new/delete)를 사용하면 메모리 누수(Memory Leak)가 발생할 위험이 있다.
특히, 예외(Exception)가 발생하는 경우 delete가 호출되지 않아 누수가 발생할 수 있다.
💡 일반 포인터의 문제점 (메모리 누수 발생)
#include <iostream>
void memoryLeak() {
int* ptr = new int(100);
std::cout << *ptr << std::endl;
// delete ptr; // ❌ 메모리 해제하지 않으면 누수 발생!
}
int main() {
memoryLeak(); // 여러 번 호출하면 메모리 누적됨
return 0;
}
🔹 위 코드는 delete ptr;이 없으면 메모리 누수가 발생할 수 있다.
💡 스마트 포인터(std::unique_ptr)를 사용하면 자동으로 메모리 해제 가능
#include <iostream>
#include <memory>
void safeMemory() {
std::unique_ptr<int> ptr = std::make_unique<int>(100);
std::cout << *ptr << std::endl;
}
int main() {
safeMemory(); // ✅ 메모리 자동 해제됨
return 0;
}
🔹 스마트 포인터를 사용하면 delete를 명시적으로 호출하지 않아도 된다.
📌 2. std::unique_ptr (단독 소유권을 가지는 스마트 포인터)
std::unique_ptr는 특정 객체에 대한 소유권을 단독으로 가지는 스마트 포인터이다.
즉, 같은 객체를 여러 unique_ptr이 가리킬 수 없으며,
소유권 이전을 위해 std::move()를 사용해야 한다.
💡 기본 문법
std::unique_ptr<데이터타입> 포인터변수 = std::make_unique<데이터타입>(초기값);
💡 예제: std::unique_ptr 사용법
#include <iostream>
#include <memory> // 스마트 포인터 사용을 위한 헤더
int main() {
// 스마트 포인터 생성
std::unique_ptr<int> ptr = std::make_unique<int>(42);
std::cout << "값: " << *ptr << std::endl;
// 소유권 이전 (std::move 사용)
std::unique_ptr<int> ptr2 = std::move(ptr);
std::cout << "ptr2가 가리키는 값: " << *ptr2 << std::endl;
// ptr은 더 이상 유효하지 않음
if (!ptr) {
std::cout << "ptr은 nullptr입니다." << std::endl;
}
return 0;
}
🔹 출력 결과
값: 42
ptr2가 가리키는 값: 42
ptr은 nullptr입니다.
💡 설명
- std::unique_ptr<int> ptr = std::make_unique<int>(42); → ptr이 42를 소유.
- std::unique_ptr<int> ptr2 = std::move(ptr); → ptr2가 소유권을 가져감.
- ptr은 nullptr이 되어 더 이상 사용할 수 없음.
⚠ 주의: std::unique_ptr는 복사(copy)가 불가능하고, 이동(move)만 가능하다.
std::unique_ptr<int> ptr2 = ptr; // ❌ 복사 불가능 (컴파일 오류)
📌 3. std::shared_ptr (참조 카운트를 사용하는 공유 스마트 포인터)
std::shared_ptr는 여러 개의 포인터가 같은 객체를 공유할 수 있도록 지원한다.
내부적으로 참조 카운트(Reference Count)를 유지하여
마지막 shared_ptr이 소멸될 때 자동으로 메모리를 해제한다.
💡 기본 문법
std::shared_ptr<데이터타입> 포인터변수 = std::make_shared<데이터타입>(초기값);
💡 예제: std::shared_ptr 사용법
#include <iostream>
#include <memory> // 스마트 포인터 사용을 위한 헤더
int main() {
std::shared_ptr<int> ptr1 = std::make_shared<int>(100);
std::cout << "ptr1 참조 카운트: " << ptr1.use_count() << std::endl;
// 새로운 shared_ptr가 같은 객체를 참조
std::shared_ptr<int> ptr2 = ptr1;
std::cout << "ptr2 추가 후 참조 카운트: " << ptr1.use_count() << std::endl;
// ptr1을 제거
ptr1.reset();
std::cout << "ptr1 해제 후 ptr2 참조 카운트: " << ptr2.use_count() << std::endl;
return 0;
}
🔹 출력 결과
ptr1 참조 카운트: 1
ptr2 추가 후 참조 카운트: 2
ptr1 해제 후 ptr2 참조 카운트: 1
💡 설명
- std::make_shared<int>(100); → ptr1이 100을 소유.
- ptr2 = ptr1; → ptr2가 같은 객체를 참조 (참조 카운트 증가).
- ptr1.reset(); → ptr1이 해제되어 참조 카운트 감소.
📌 4. std::weak_ptr (순환 참조 문제 해결)
std::shared_ptr는 서로가 서로를 참조할 경우 순환 참조(Circular Reference) 문제가 발생할 수 있다.
이를 해결하기 위해 약한 참조(Weak Reference)를 제공하는 std::weak_ptr를 사용한다.
💡 예제: std::weak_ptr을 사용한 순환 참조 해결
#include <iostream>
#include <memory>
class Node {
public:
int value;
std::shared_ptr<Node> next;
Node(int val) : value(val) {
std::cout << "Node 생성: " << value << std::endl;
}
~Node() {
std::cout << "Node 삭제: " << value << std::endl;
}
};
int main() {
std::shared_ptr<Node> node1 = std::make_shared<Node>(10);
std::shared_ptr<Node> node2 = std::make_shared<Node>(20);
// ❌ 순환 참조 발생 (메모리 누수 가능)
// node1->next = node2;
// node2->next = node1;
// ✅ `std::weak_ptr`을 사용하여 순환 참조 방지
node1->next = node2;
node2->next = std::weak_ptr<Node>(node1);
return 0;
}
🔹 출력 결과
Node 생성: 10
Node 생성: 20
Node 삭제: 20
Node 삭제: 10
💡 설명
- std::weak_ptr은 참조 카운트를 증가시키지 않아 순환 참조를 방지할 수 있다.
📌 5. 정리
스마트 포인터 | 특징 | 사용 목적 |
std::unique_ptr | 단독 소유 | 객체를 하나의 포인터만 관리 |
std::shared_ptr | 참조 카운트 증가 | 여러 개의 포인터가 공유 가능 |
std::weak_ptr | 순환 참조 방지 | shared_ptr과 함께 사용하여 순환 참조 해결 |
'프로그래밍 언어 > C++' 카테고리의 다른 글
C++ 초급 - 6. 배열과 문자열 (2 - 다차원 배열 (int matrix[3][3];)) (0) | 2025.02.22 |
---|---|
C++ 초급 - 6. 배열과 문자열 (1 - 배열 (int arr[5];)) (0) | 2025.02.12 |
C++ 초급 - 5. 포인터와 참조 (Pointers and References) (3 - 참조 (& 연산자)) (0) | 2025.02.12 |
C++ 초급 - 5. 포인터와 참조 (Pointers and References) (2 - 동적 메모리 할당 (new, delete)) (0) | 2025.02.12 |
C++ 초급 - 5. 포인터와 참조 (Pointers and References) (1 - 포인터 기본 개념 (*, &, nullptr)) (0) | 2025.02.12 |