Modern C++ 강의 정리 – 스마트 포인터 (Smart Pointer)

유니크 포인터 std::unique_ptr<T>

객채에 대한 원시 포인터를 다른 포인터와 공유하지 않는다. Scope를 벗어날 때 해당 객체도 같이 삭제한다.

class Project {
public:
    std::string name{};
};

int main()
{
    Project* project = new Project();
    std::unique_ptr<Project> p1(new Project());
    return 0;
}

unique_ptr에 대해 쓸 수 있는 멤버 함수들이 있다.

reset() – 원시 포인터를 소멸시킴. 인자를 넣어서 새로운 원시 포인터를 할당할 수도 있음.
get() – 원시 포인터를 반환함. 함수에 인자로 넣을 때 쓰기에 좋은 듯함. 물론 함수 호출 후에 필요없으면 std::move를 쓰는게 더 좋음.
release() – 원시 포인터에 대한 소유를 없애고, 원시 포인터를 리턴함.

공유 포인터 std::shared_ptr<T>

다른 객체와 원시 포인터를 공유할 수 있다. 원시 포인터의 객체는 참조 카운트가 0이 될 때 소멸된다. 예를 들어 어떤 프로젝트를 사람에게 할당하는 것을 클래스로 표현하면 다음과 같이 될 수 있다.

class Project {
public:
    std::string name;
    Project(std::string name): name(name) {
    }
};
class Person {
public:
    std::shared_ptr<Project> project;
};

int main()
{
    Project* project = new Project("abc");
    Person p1, p2;
    p1.project.reset(project);
    p2.project = p1.project;
    std::cout << p1.project.get()->name << endl;
    std::cout << p2.project.get()->name << endl;
    return 0;
}

shared_ptr를 사용했을 때 발생하는 문제로는 상호 참조가 있다. 상호 참조를 없애기 위해 weak_ptr를 사용한다.

std::weak_ptr
class B;
class A {
public:
    std::shared_ptr<B> b;
};
class B {
public:
    std::shared_ptr<A> a;
};

int main()
{
    std::shared_ptr<A> a(new A());
    std::shared_ptr<B> b(new B());
    a.get()->b = b;
    b.get()->a = a;
    return 0;
}

위와 같은 경우에서 Main이 종료되었을 때 각 객체의 멤버는 여전히 서로를 참조하기 때문에 각자 레퍼런스 카운트가 1로 객체 소멸이 발생하지 않는다. 이 문제를 해결하기 위해 weak_ptr로 참조를 하게 만든다. 물론 weak_ptr도 lock 메소드를 통해 레퍼런스 카운트를 1 증가시킬 수 있다.