C++에서 메모리 관리를 보다 효율적이고 안전하게 하기 위해 제공되는 클래스 템플릿이다. 일반적인 포인터와 달리, 스마트 포인터는 메모리의 소유권과 생명주기를 관리하여 메모리 누수와 같은 문제를 예방한다.
소유권이 유일한 포인터이다. 하나의 unique_ptr만이 특정 객체를 소유할수 있다. 복사가 불가능하고, 이동(std::move)만 가능하다.
기본 사용 예시
#include <iostream> #include <memory> using namespace std; int main() { // unique_ptr 생성 unique_ptr<int> ptr1 = make_unique<int>(20); // 소유권 이동 (move 사용) unique_ptr<int> ptr2 = move(ptr1); if (!ptr1) { cout << "ptr1은 이제 비어 있습니다." << endl; } cout << "ptr2의 값: " << *ptr2 << endl; return 0; }
일반 클래스에서 사용 예시
#include <iostream> #include <memory> using namespace std; class MyClass { public: MyClass(int val) : value(val) { cout << "MyClass 생성: " << value << endl; } ~MyClass() { cout << "MyClass 소멸: " << value << endl; } void display() const { cout << "값: " << value << endl; } private: int value; }; int main() { // unique_ptr로 MyClass 객체 관리 unique_ptr<MyClass> myObject = make_unique<MyClass>(42); // MyClass 멤버 함수 호출 myObject->display(); // 소유권 이동 unique_ptr<MyClass> newOwner = move(myObject); if (!myObject) { cout << "myObject는 이제 비어 있습니다." << endl; } newOwner->display(); // 범위를 벗어나면 newOwner가 관리하는 메모리 자동 해제 return 0; }
여러 포인터가 동일한 객체를 공유할 수 있다. 참조 카운트를 사용하여 객체가 더 이상 필요하지 않을 때 메모리를 해제한다.
기본 사용 예시
#include <iostream> #include <memory> // shared_ptr 사용 using namespace std; int main() { // shared_ptr 생성 shared_ptr<int> ptr1 = make_shared<int>(10); // ptr1의 참조 카운트 출력 cout << "ptr1의 참조 카운트: " << ptr1.use_count() << endl; // 출력: 1 // ptr2가 ptr1과 리소스를 공유 shared_ptr<int> ptr2 = ptr1; cout << "ptr2 생성 후 참조 카운트: " << ptr1.use_count() << endl; // 출력: 2 // ptr2가 범위를 벗어나면 참조 카운트 감소 ptr2.reset(); cout << "ptr2 해제 후 참조 카운트: " << ptr1.use_count() << endl; // 출력: 1 // 범위를 벗어나면 ptr1도 자동 해제 return 0; }일반 클래스에서 사용 예시
#include <iostream> #include <memory> using namespace std; class MyClass { public: MyClass(int val) : value(val) { cout << "MyClass 생성: " << value << endl; // 출력: MyClass 생성: 42 } ~MyClass() { cout << "MyClass 소멸: " << value << endl; // 출력: MyClass 소멸: 42 } void display() const { cout << "값: " << value << endl; // 출력: 값: 42 } private: int value; }; int main() { // shared_ptr로 MyClass 객체 관리 shared_ptr<MyClass> obj1 = make_shared<MyClass>(42); // 참조 공유 shared_ptr<MyClass> obj2 = obj1; cout << "obj1과 obj2의 참조 카운트: " << obj1.use_count() << endl; // 출력: 2 obj2->display(); // 출력: 값: 42 // obj2를 해제해도 obj1이 객체를 유지 obj2.reset(); cout << "obj2 해제 후 obj1의 참조 카운트: " << obj1.use_count() << endl; // 출력: 1 return 0; }순환 참조
#include <iostream> #include <memory> class B; // Forward declaration class A { public: std::shared_ptr<B> b_ptr; ~A() { std::cout << "A destroyed\n"; } }; class B { public: std::shared_ptr<A> a_ptr; ~B() { std::cout << "B destroyed\n"; } }; int main() { auto a = std::make_shared<A>(); auto b = std::make_shared<B>(); a->b_ptr = b; b->a_ptr = a; // main 함수가 끝나도 A와 B는 서로 참조 중이라 메모리 해제가 안 됨 return 0; }main 함수가 끝나도 a,b는 서로 참조중이라 메모리 해제가 되지 않는다.
shared_ptr과 함께 사용되며, 참조 카운트를 증가시키지 않는다. 따라서 순환 참조를 방지할 수 있다.
shared_ptr가 소멸된 후에도 객체에 접근할 수 있는 방법을 제공한다.
lock함수로 유효성을 확인하고 사용
#include <iostream> #include <memory> class A { public: void say_hello() { std::cout << "Hello from A\n"; } }; class B { public: std::weak_ptr<A> a_ptr; void useA() { if (auto a_shared = a_ptr.lock()) // 유효성 확인 { a_shared->say_hello(); } else { std::cout << "A is no longer available.\n"; } } }; int main() { std::shared_ptr<B> b = std::make_shared<B>(); { std::shared_ptr<A> a = std::make_shared<A>(); b->a_ptr = a; b->useA(); // A가 유효하므로 Hello 출력 } // A는 scope를 벗어나며 소멸됨 b->useA(); // A는 이미 소멸되었기 때문에 메시지 출력 }C++에서 std::weak_ptr 객체를 통해 사용되는 함수로, 약한 포인터가 참조하고 있는 shared_ptr 객체가 여전히 유효한지 확인하고, 유효하면 해당 객체에 대한 shared_ptr을 반환한다.
순환 참조 해결
#include <iostream> #include <memory> class B; // Forward declaration class A { public: std::shared_ptr<B> b_ptr; ~A() { std::cout << "A destroyed\n"; } }; class B { public: std::weak_ptr<A> a_ptr; // weak_ptr로 변경 ~B() { std::cout << "B destroyed\n"; } }; int main() { auto a = std::make_shared<A>(); auto b = std::make_shared<B>(); a->b_ptr = b; b->a_ptr = a; // weak_ptr로 참조 return 0; }
스마트 포인트 사용시 main함수에서 shared_ptr로 선언된 객체가 스코프를 벗어날 때, 해당 shared_ptr가 먼저 소멸되고 shared_ptr이 소유하는 객체의 참조포인트가 감소하고, 참조 카운트가 0이되면 해당 객체의 소멸자가 호출된다.