학습
C++ 스마트 포인터 (Smart Pointers)
C++ 스마트 포인터 (Smart Pointers) — 중급
스마트 포인터 (Smart Pointers)
1. 도입 (왜 배워야 하는가)
스마트 포인터는 C++에서 메모리 관리를 보다 안전하고 효율적으로 할 수 있게 도와주는 도구입니다. 특히, 객체의 생명 주기를 자동으로 관리하여 메모리 누수(memory leak)와 같은 문제를 방지합니다. 실무에서는 메모리 관리가 매우 중요합니다. 메모리 누수가 발생하면 시스템 성능이 저하되거나, 프로그램이 비정상적으로 종료될 수 있습니다. 따라서 스마트 포인터를 사용하지 않으면 객체의 메모리를 해제하지 못해 메모리 누수가 발생하거나, 이미 해제된 메모리를 접근하는 오류가 발생할 수 있습니다.
2. 핵심 개념 설명
스마트 포인터는 C++에서 관리되는 포인터로, 주로 다음과 같은 세 가지 유형이 있습니다:
- std::unique_ptr
- std::shared_ptr
- std::weak_ptr
std::unique_ptr<int> ptr(new int(10));: 정수형 10을 가리키는unique_ptr생성.std::cout << "값: " << *ptr << std::endl;: 스마트 포인터가 가리키는 값을 출력.std::unique_ptr<int> ptr2 = std::move(ptr);:ptr의 소유권을ptr2로 이전.std::cout << "ptr2의 값: " << *ptr2 << std::endl;:ptr2가 이제 소유한 값 출력.- 주의:
ptr은 nullptr가 되어 더 이상 사용할 수 없음. - 잘못된 코드
- 올바른 코드
- 잘못된 코드
- 올바른 코드
- 스마트 포인터는 메모리 관리를 자동으로 해주어 안전성을 높입니다.
std::unique_ptr,std::shared_ptr,std::weak_ptr의 세 가지 유형이 있습니다.- 소유권 이전과 순환 참조 문제를 주의해야 합니다.
- 다음에 학습하면 좋은 주제는 "C++의 RAII(자원 획득은 초기화)"입니다.
- 한 객체에 대해 단 하나의 포인터만 가질 수 있습니다. 소유권이 이전될 수 있지만, 복사할 수는 없습니다. - 비유: 독점적인 소유권. 한 사람이 한 물건을 독점적으로 소유하는 것과 같습니다.
- 여러 포인터가 같은 객체를 공유할 수 있습니다. 참조 카운트를 사용하여 객체의 생명 주기를 관리합니다. - 비유: 공동 소유. 여러 사람이 함께 소유하는 물건처럼, 누가 사용하더라도 소유권이 유지됩니다.
- std::shared_ptr와 함께 사용되며, 소유권을 가지지 않는 포인터입니다. 주로 순환 참조를 방지하는 데 사용됩니다.
- 비유: 친구의 물건을 잠깐 빌리는 것. 친구가 물건을 계속 가지고 있는 한 사용하지만, 소유권은 없습니다.
| 스마트 포인터 유형 | 소유권 | 복사 가능 여부 |
|---|---|---|
| std::unique_ptr | Yes | No |
| std::shared_ptr | Yes | Yes |
| std::weak_ptr | No | No |
3. 기본 코드 예제
다음은 std::unique_ptr의 기본 사용법을 보여주는 코드입니다.
#include <iostream>
#include <memory>
int main() {
std::unique_ptr<int> ptr(new int(10)); // int 타입의 스마트 포인터 생성
std::cout << "값: " << ptr << std::endl; // 스마트 포인터가 가리키는 값 출력
// ptr의 소유권을 다른 unique_ptr로 이전
std::unique_ptr<int> ptr2 = std::move(ptr);
std::cout << "ptr2의 값: " << ptr2 << std::endl; // ptr2가 소유권을 받음
// ptr을 더 이상 사용할 수 없음
// std::cout << *ptr; // 오류 발생: ptr은 nullptr임
return 0;
}
실행 결과
값: 10
ptr2의 값: 10
코드 한 줄씩 동작 설명
4. 실전 활용 예제
스마트 포인터는 다양한 실무 상황에서 활용됩니다.
예제 1: 쇼핑몰 장바구니
장바구니에 담긴 상품을 관리할 때, 각 상품 객체를 shared_ptr로 관리하면 여러 사용자가 장바구니를 공유할 수 있습니다.
#include <iostream>
#include <memory>
#include <vector>
class Product {
public:
Product(std::string name) : name(name) {}
std::string name;
};
int main() {
std::vector<std::shared_ptr<Product>> cart;
cart.push_back(std::make_shared<Product>("상품1"));
cart.push_back(std::make_shared<Product>("상품2"));
for (const auto& product : cart) {
std::cout << "장바구니 상품: " << product->name << std::endl;
}
return 0;
}
예제 2: 순환 참조 방지
weak_ptr를 사용하여 순환 참조 문제를 해결할 수 있습니다.
#include <iostream>
#include <memory>
class A;
class B {
public:
std::weak_ptr<A> aPtr; // A의 weak_ptr
};
class A {
public:
std::shared_ptr<B> bPtr; // B의 shared_ptr
};
int main() {
std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<B> b = std::make_shared<B>();
a->bPtr = b; // A가 B를 소유
b->aPtr = a; // B가 A를 weak하게 참조
return 0;
}
5. 흔한 실수와 주의사항
1. 소유권 이전을 잊음
std::unique_ptr<int> p1(new int(5));
std::unique_ptr<int> p2 = p1; // 오류: 복사할 수 없음
std::unique_ptr<int> p1(new int(5));
std::unique_ptr<int> p2 = std::move(p1); // 소유권 이전
2. shared_ptr의 순환 참조
class A { std::shared_ptr<B> b; };
class B { std::shared_ptr<A> a; }; // 순환 참조 발생
class A { std::shared_ptr<B> b; };
class B { std::weak_ptr<A> a; }; // weak_ptr로 수정