본문 바로가기
-----ETC-----/C++ 마스터 시리즈

[C++ 마스터]Day 25: 스마트 포인터 (unique_ptr, shared_ptr)

by cogito21_cpp 2024. 8. 1.
반응형

스마트 포인터 (Smart Pointers)

스마트 포인터는 메모리 관리 문제를 해결하기 위해 C++11에서 도입된 기능입니다. 스마트 포인터는 자동으로 메모리를 관리하여, 메모리 누수와 같은 문제를 줄여줍니다. C++ 표준 라이브러리는 unique_ptr, shared_ptr, weak_ptr라는 세 가지 주요 스마트 포인터를 제공합니다.

 

unique_ptr

unique_ptr는 소유권이 단 하나의 객체에만 있는 스마트 포인터입니다. 한 번에 한 객체만 소유권을 가질 수 있으며, 소유권을 다른 객체로 이동할 수 있습니다. unique_ptr는 가장 가볍고, 소유권 이전을 명확하게 할 수 있는 장점이 있습니다.

 

1. unique_ptr 기본 사용법

#include <iostream>
#include <memory>

using namespace std;

class MyClass {
public:
    MyClass() {
        cout << "Constructor called" << endl;
    }
    ~MyClass() {
        cout << "Destructor called" << endl;
    }
    void display() {
        cout << "Hello from MyClass" << endl;
    }
};

int main() {
    unique_ptr<MyClass> ptr1(new MyClass());  // unique_ptr 생성
    ptr1->display();  // 포인터를 통해 멤버 함수 호출

    // unique_ptr 이동 (ptr1의 소유권을 ptr2로 이동)
    unique_ptr<MyClass> ptr2 = move(ptr1);
    if (!ptr1) {
        cout << "ptr1 is now null" << endl;
    }
    ptr2->display();

    return 0;
}

 

shared_ptr

shared_ptr는 여러 개의 스마트 포인터가 하나의 객체를 공유할 수 있게 합니다. 객체의 참조 카운터를 관리하여, 마지막 shared_ptr이 소멸될 때 객체가 자동으로 삭제됩니다.

 

1. shared_ptr 기본 사용법

#include <iostream>
#include <memory>

using namespace std;

class MyClass {
public:
    MyClass() {
        cout << "Constructor called" << endl;
    }
    ~MyClass() {
        cout << "Destructor called" << endl;
    }
    void display() {
        cout << "Hello from MyClass" << endl;
    }
};

int main() {
    shared_ptr<MyClass> ptr1(new MyClass());  // shared_ptr 생성
    {
        shared_ptr<MyClass> ptr2 = ptr1;  // 참조 카운터 증가
        ptr2->display();
        cout << "ptr2 going out of scope" << endl;
    }  // ptr2가 범위를 벗어나면서 참조 카운터 감소
    cout << "Back in main" << endl;
    ptr1->display();

    return 0;
}

 

weak_ptr

weak_ptrshared_ptr와 함께 사용되어 순환 참조를 방지합니다. weak_ptr는 객체를 소유하지 않으며, shared_ptr가 관리하는 객체를 참조합니다. 이를 통해 순환 참조로 인한 메모리 누수를 방지할 수 있습니다.

 

1. weak_ptr 기본 사용법

#include <iostream>
#include <memory>

using namespace std;

class MyClass {
public:
    MyClass() {
        cout << "Constructor called" << endl;
    }
    ~MyClass() {
        cout << "Destructor called" << endl;
    }
    void display() {
        cout << "Hello from MyClass" << endl;
    }
};

int main() {
    shared_ptr<MyClass> ptr1(new MyClass());  // shared_ptr 생성
    weak_ptr<MyClass> wptr = ptr1;  // weak_ptr 생성

    // weak_ptr을 shared_ptr로 변환하여 객체에 접근
    if (shared_ptr<MyClass> sptr = wptr.lock()) {
        sptr->display();
    } else {
        cout << "The object has been deleted." << endl;
    }

    return 0;
}

 

예제 문제

문제 1: unique_ptr을 사용하여 객체 생성 및 소멸 확인

unique_ptr을 사용하여 객체를 생성하고, 객체의 소멸이 자동으로 이루어지는지 확인하는 프로그램을 작성하세요.

 

해설:

#include <iostream>
#include <memory>

using namespace std;

class Test {
public:
    Test() {
        cout << "Constructor called" << endl;
    }
    ~Test() {
        cout << "Destructor called" << endl;
    }
    void show() {
        cout << "Hello from Test" << endl;
    }
};

int main() {
    unique_ptr<Test> ptr1(new Test());
    ptr1->show();

    unique_ptr<Test> ptr2 = move(ptr1);
    if (!ptr1) {
        cout << "ptr1 is now null" << endl;
    }
    ptr2->show();

    return 0;
}

 

이 프로그램은 unique_ptr을 사용하여 객체를 생성하고, 소멸자가 자동으로 호출되는지 확인합니다.

 

문제 2: shared_ptr을 사용하여 참조 카운터 확인

shared_ptr을 사용하여 여러 포인터가 동일한 객체를 참조할 때, 참조 카운터가 어떻게 변하는지 확인하는 프로그램을 작성하세요.

 

해설:

#include <iostream>
#include <memory>

using namespace std;

class Test {
public:
    Test() {
        cout << "Constructor called" << endl;
    }
    ~Test() {
        cout << "Destructor called" << endl;
    }
    void show() {
        cout << "Hello from Test" << endl;
    }
};

int main() {
    shared_ptr<Test> ptr1(new Test());
    {
        shared_ptr<Test> ptr2 = ptr1;
        cout << "ptr1 use count: " << ptr1.use_count() << endl;
        cout << "ptr2 use count: " << ptr2.use_count() << endl;
    }
    cout << "ptr1 use count after ptr2 out of scope: " << ptr1.use_count() << endl;

    return 0;
}

 

이 프로그램은 shared_ptr을 사용하여 참조 카운터가 어떻게 변하는지 확인합니다.

 

문제 3: weak_ptr을 사용하여 순환 참조 방지

shared_ptrweak_ptr을 사용하여 순환 참조를 방지하는 프로그램을 작성하세요.

 

해설:

#include <iostream>
#include <memory>

using namespace std;

class B;  // 클래스 B의 전방 선언

class A {
public:
    shared_ptr<B> bptr;
    ~A() {
        cout << "A destroyed" << endl;
    }
};

class B {
public:
    weak_ptr<A> aptr;
    ~B() {
        cout << "B destroyed" << endl;
    }
};

int main() {
    {
        shared_ptr<A> a(new A());
        shared_ptr<B> b(new B());
        a->bptr = b;
        b->aptr = a;
    }
    // A와 B의 소멸자가 호출되며, 순환 참조가 발생하지 않음을 확인

    return 0;
}

 

이 프로그램은 shared_ptrweak_ptr을 사용하여 순환 참조를 방지합니다.

 

다음 단계

25일차의 목표는 C++의 스마트 포인터에 대해 학습하는 것이었습니다. 다음 날부터는 멀티스레딩과 동기화에 대해 다룰 것입니다.

 

내일은 "멀티스레딩과 동기화"에 대해 다룰 예정입니다. 질문이나 피드백이 있으면 댓글로 남겨 주세요!

반응형