프로그래밍 공부/C++ 프로그래밍

Direct X 수업 2일차💻 스마트 포인터

데브준우 2022. 6. 15. 03:21

오늘은 스마트 포인터와 벡터,리스트,언오더드맵에 대해 배웠다.
이것들에 대해 정리한다.

스마트 포인터


#include<memory> 를 해주어야 한다.

unique_ptr, shared_ptr, weak_ptr 3개가 있다.
하나하나 파보자.

unique_ptr

개념

유니크 ptr
참조자를 1개만 허용해준다.
스마트포인터로 참조자를 1개 만들고나서 , 다른 스마트포인터로 앞의 참조자가 참조하려는 인스턴스를 참조하려고하면 허용하지 않는다.

1개만 참조를 해야하는 경우에 사용한다.

생성하기

int main()
{
   
    unique_ptr<Animal> p = make_unique<Animal>(10); //Animal이라는 클래스를 생성함
    p.get()->YourMaxAge();
    p->YourMaxAge();
    
 }


유니크 ptr로 1일차에 만들었던 Animal이라는 클래스를 생성 해주었다.
make_unique<T> 로 생성한다.
인스턴스에 접근하는법은
p.get() -> 함수or변수;
p -> 함수or변수;

둘 중 아무거나 상관없다.
당연히 후자가 편리하다.

실행을 해보면 delete를 해주지 않아도 알아서 소멸자가 나오는 것을 볼 수 있다. (이래서 스마트?)

Animal* p = make_unique<Animal>(10).get();

이런식으로도 생성 가능하다. 다만 unique_ptr로 생성한것을 일반 포인터로 받았기 때문에 소유권 이전에 문제가 있다.

참조 옮기기 (소유권 이전시키기)

 unique_ptr<Animal> p = make_unique<Animal>(10); //Animal이라는 클래스를 생성함
    p.get()->YourMaxAge();
    p->YourMaxAge();
    unique_ptr<Animal> p2 = p; //허용하지 않음
    unique_ptr<Animal> p2 = move(p);
p2가 그대로 p를 참조하는 것을 허용하지 않는다.


참조자를 1개만 인정하기 때문에 p2가 그대로 p를 참조하는 것을 허용하지는 않는다.
대신 move()를 이용해 참조자를 옮기는 것은 가능하다.

int main()
{
    unique_ptr<Animal> p = make_unique<Animal>(10); //Animal이라는 클래스를 생성함
    unique_ptr<Animal> p2 = move(p);
    p2->YourMaxAge();
}

p에서 p2로 참조자를 옮기고 p2에서 함수를 실행해보면 아주 잘 작동한다.

Animal* p2 = move(p).get(); // 참조하는 애의 주소를 리턴해줌 // 소유주를 옮기는게 문제가 됨

get() 은 참조하는 주소를 리턴해준다.
위와 같은 코드로도 참조자를 변경 해줄 수 있지만 이 역시 다시 다른 참조자로 옮기는 것이 문제가 된다.
(unique_ptr이 아닌 포인터 이기 때문에)

shared_ptr


우리가 평소에 사용하던 포인터와 비슷하다.
shared_ptr로 참조자를 만들면 레퍼런스 카운터라는것을 만든다.
참조하는 개수 만큼 레퍼런스 카운터를 가지고 있다가 참조가 사라지면 레퍼런스 카운터가 감소한다.
참조하는 수가 0이 되면, 인스턴스도 자동으로 삭제를 해준다.(스마트 하다)

shared_ptr의 생성

int main()
{
  shared_ptr<Animal> p = make_shared<Animal>(10); //Animal이라는 클래스를 생성함
    Animal* p2 = p.get();

    cout << "p의 주소 : " << p << endl;
    cout << "p2의 주소 : " << p2 << endl;
    
    p->YourMaxAge();
    p2->YourMaxAge();
 }


make_shared로 생성 할 수 있다.
unique_ptr 처럼 참조자가 1개만 되는것이 아니기 때문에 일반 포인터 p2가 shared_ptr로 생성한 p의 주소를 참조 하게 해도 문제가 없다


값 , 주소가 모두 같은 것을 볼 수 있다.
그리고 역시 delete가 없음에도 불구하고 소멸이 잘 이루어지며 오류가 없는 것을 확인 할 수 있다.

weak_ptr


위에 shared_ptr로 참조하면 레퍼런스 카운터가 늘어나지만, Weak_ptr로 참조하면 레퍼런스 카운터가 증가하지 않음.
반대로 참조를 해제해도 레퍼런스 카운터가 감소하지 않음(간접 참조)


위의 예를 실험하기 위해 코드를 입력한다.

int main()
{
    weak_ptr<Animal> wp;
    {
        shared_ptr<Animal> p = make_shared<Animal>(10); //Animal이라는 클래스를 생성함
        wp = p;
        cout << "p 삭제" << endl;
    }

    cout << "wp 삭제" << endl;
}


shared_ptr은 레퍼런스 카운트가 0이 됐을때 할당이 해제 된다.
위의 코드에서 weak_ptr은 메인 함수에 존재하고
shared_ptr은 저 소괄호 안에 존재한다.

1. shared_ptr로 클래스를 생성한다. (레퍼런스 카운트 1)
2.weak_ptr이 p를 참조하게 한다. (여전히 레퍼런스 카운트 1)
3. p 삭제라는 글이 출력되고 나서 shared ptr은 소멸되기 때문에 레퍼런스 카운트가 0이 되어서 메모리 할당이 해제되고 소멸자를 호출한다.
4. 그 다음에 wp 삭제가 출력된다.


위 설명과 같이 p삭제가 출력 되고나서 소멸자가 호출되고 wp삭제가 출력 됐다.
weak_ptr의 참조는 레퍼런스 카운트에 영향을 주지 못한다.