1. 선언 방법
std::shared_ptr<A> p1(new A());
std::shared_ptr<A> p2(p1);
// 문법 오류 X
shared_ptr은 기본적으로 해당 객체가 몇개 선언되었는지에 대한 count를 제공한다. 해당 count가 0이 되면 해당 객체를 가리키는 포인터가 전부 파괴되었다고 생각하고 해당 포인터를 메모리에서 해제한다.
std::cout << p1.use_count(); // 2
std::cout << p2.use_count(); // 2
shared_ptr 또한 unique_ptr처럼 make_shared 함수를 제공한다.
auto p1 = std::make_shared<A>();
2. std::enable_shared_from_this
A* a = new A();
std::shared_ptr<A> pa1(a);
std::shared_ptr<A> pa2(a);
std::cout << pa1.use_count() << std::endl; // 1
std::cout << pa2.use_count() << std::endl; // 1
shared_ptr에 만약 어떠한 객체의 포인터 값을 전달한다면 각 shared_ptr의 참조 계수가 공유되지 않는다(각각 서로가 1이라는 참조계수를 갖게 된다). 따라서 shared_ptr을 생성할 때 이런 식으로 포인터 값을 직접적으로 전달하는 행위는 지양해야 한다.
하지만 어쩔 수 없이 shared_ptr에 포인터값을 직접 전달해야 하는 상황이 있을 수 있다. 그럴 때는 객체에 std::enable_shared_from_this를 상속해주면 해결할 수 있다.
class A : public std::enable_shared_from_this<A> {
};
std::shared_ptr<A> pa1 = std::make_shared<A>();
std::shared_ptr<A> pa2 = pa1->get_shared_ptr();
std::cout << pa1.use_count() << std::endl; // 2
std::cout << pa2.use_count() << std::endl; // 2
이러면 정상적으로 두 shared_ptr이 사용 계수를 서로 공유하게 되고 double_free 버그를 방지할 수 있다.
3. 순환 참조 shared_ptr
class A {
int *data;
std::shared_ptr<A> other;
public:
A() {
data = new int[100];
}
~A() {
delete[] data;
}
void set_other(std::shared_ptr<A> o) { other = o; }
};
int main() {
std::shared_ptr<A> pa = std::make_shared<A>();
std::shared_ptr<A> pb = std::make_shared<A>();
pa->set_other(pb);
pb->set_other(pa);
// 상호 참조 발생!
}
shared_ptr의 경우 참조 계수라는 것을 통해서 메모리에서 free를 할지 말지 결정한다. 근데 위와 같이 shared_ptr이 순환 참조를 할 경우, 참조 계수가 절대 0이 될 수 없다. (각자가 각자의 메모리를 가리키고 있기 때문에 서로 해제될 수 없다.)
이런 상황일 경우, weak_ptr을 통해서 설계를 할 것을 고려해야 한다.
4. weak_ptr
weak_ptr은 shared_ptr이나 weak_ptr을 복사 생성해서 생성할 수 있고, weak_ptr이 무엇을 가리키던지 간에 사용 계수를 증가시키지 않는다.
auto o = std::make_shared<A>();
std::weak_ptr<A> other = o;
// 반드시 shared_ptr이나 같은 weak_ptr을 복사생성해야 생성 가능
// weak_ptr을 생성해서 o를 참조해도 참조 계수 증가 X!
std::shared_ptr<A> b = o.lock(); // o.lock()은 shared_ptr이 가리키는 메모리가 존재하면
// 해당 shared_ptr을 리턴하고
// 존재하지 않는다면 아무것도 가리키지 않는 shared_ptr을 가리킨다.
if(b) {
// 메모리가 해제되지 않은 경우(객체가 존재하는 경우)
}
else {
// 메모리가 해제된 경우
}
따라서 위에서 설명한 순환 참조 shared_ptr 문제를 발생시키지 않는 방식이라고 할 수 있다.
'프로그래밍 언어 > C++' 카테고리의 다른 글
C++ 11 / 스레드 (0) | 2023.02.16 |
---|---|
C++ / std::function (0) | 2023.02.16 |
C++ 11 / 스마트 포인터 unique_ptr (0) | 2023.02.15 |
C++ 11 / 보편적 참조(universal reference)와 forward (0) | 2023.02.15 |
C++ 11 / move함수 (0) | 2023.02.15 |
댓글