내 연락처 정보
우편메소피아@프로톤메일.com
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
원시 포인터의 단점:
delete
리소스를 해제하여 리소스 누수 유발delete
이전에 프로그램이 정상적으로 종료되었습니다(예:if
가운데return
) 또는 너무 늦기 전에 비정상적으로 종료delete
, 자원 유출로 이어짐이때 스마트 포인터가 필요합니다. 스마트 포인터의 지능이라는 단어는 사용자가 리소스 릴리스에 주의를 기울일 필요가 없다는 사실에 주로 반영됩니다. 스마트 포인터는 프로그램 논리가 어떻게 실행되든 리소스 릴리스를 완벽하게 관리하는 데 도움이 되기 때문입니다. , 정상적으로 실행되거나 예외가 생성됩니다.리소스가 만료되면(함수 범위 또는 프로그램 종료) 해제됩니다.
간단한 스마트 포인터를 직접 구현해보자
template<typename T>
class CSmartPtr
{
public:
CSmartPtr(T* ptr = nullptr) : mptr(ptr) {}
~CSmartPtr() { delete mptr; }
private:
T* mptr;
};
int main()
{
CSmartPtr<int> ptr(new int());
/*其它的代码...*/
/*
由于ptr是栈上的智能指针对象,不管是函数正常执行完,
还是运行过程中出现异常,栈上的对象都会自动调用析构函数,
在析构函数中进行了delete操作,保证释放资源
*/
return 0;
}
따라서 위의 특성으로 인해 스마트 포인터는 일반적으로 스택에 정의됩니다. 동시에,스마트 포인터는 클래스 객체입니다 , 이 클래스의 생성자에 포인터가 전달되고 전달된 포인터가 소멸자에서 해제됩니다.이러한 유형의 객체는 스택에 할당되고 해제되므로 함수(또는 프로그램)가 끝나면 자동으로 해제됩니다.
그렇다면 스마트 포인터를 힙에 정의할 수 있습니까?예를 들어CSmartPtr* p = new CSmartPtr(new int);
, 컴파일은 통과할 수 있지만 여기서 정의는p
스마트 포인터 유형이기는 하지만 본질적으로 원시 포인터이므로p
그래도 수동으로 해야함delete
, 처음에 베어 포인터로 직면했던 문제로 돌아왔으므로 이렇게 사용하지 마세요.
물론 스마트 포인터는 원시 포인터와 유사해야 하며 원시 포인터의 공통 기능도 제공해야 합니다.*
그리고->
두 연산자의 오버로드된 함수는 사용 시 원시 포인터와 동일합니다.
template<typename T>
class CSmartPtr
{
public:
CSmartPtr(T* ptr = nullptr) : mptr(ptr) {}
~CSmartPtr() { delete mptr; }
T& operator*() { return *mptr; }
T* operator->() { return mptr; }
private:
T* mptr;
};
int main()
{
CSmartPtr<int> ptr(new int());
*ptr = 20; // operator*()一定要返回引用,这样才可以赋值
cout << *ptr << endl; // 20
class Test
{
public:
void test() { cout << "call Test::test()" << endl; }
};
CSmartPtr<Test> ptr2 = new Test();
(*ptr2).test(); // (*ptr2)取出Test对象,用对象调用方法
// operator->()返回的是一个指针,实现了用指针调用函数
// 即(ptr2.operator->()) -> test()
ptr2->test();
return 0;
}
이전 섹션에서 구현한 스마트 포인터는 사용 중인 일반 원시 포인터와 매우 유사하지만 여전히 큰 문제가 있습니다. 다음 코드를 참조하세요.
CSmartPtr<int> p1(new int());
CSmartPtr<int> p2(p1); // 拷贝构造
기본 복사 생성자가 얕은 복사를 수행하기 때문에 실행 중인 코드가 직접 충돌합니다.p1
그리고p2
동일하게 유지new int
자원,p2
파괴는 먼저 자원을 해제한 다음p1
파괴되면 다음과 같이 된다.delete
와일드 포인터, 프로그램 충돌
그렇다면 얕은 복사로 인해 발생하는 문제를 어떻게 해결할 수 있을까요?고쳐 쓰기CSmartPtr
복사 생성자를 살펴보세요
CSmartPtr(const CSmartPtr<T>& src) { mptr = new T(*src.mptr); }
이제 코드는 정상적으로 실행되지만 이 시점에서는p1
그리고p2
서로 다른 두 가지 리소스가 관리됩니다. 사용자가 이를 이해하지 못하면 다음과 같이 잘못 생각하게 됩니다.p1
그리고p2
동일한 리소스 관리
그러므로 복사 생성자를 다음과 같이 작성하는 것은 잘못된 것입니다.
그렇다면 스마트 포인터의 얕은 복사 문제를 어떻게 해결할 수 있을까요? 두 가지 방법:참조 카운팅이 없는 스마트 포인터, 참조 카운팅이 있는 스마트 포인터
먼저 이 부분을 살펴보겠습니다참조 카운팅이 없는 스마트 포인터
auto_ptr
(C++98, 이제 더 이상 사용되지 않음)scoped_ptr
(부스트 라이브러리)unique_ptr
(C++11, 권장)다음을 사용할 때 헤더 파일을 포함하십시오.#include <memory>
먼저 이 코드를 생각해 봅시다
auto_ptr<int> ptr1(new int());
auto_ptr<int> ptr2(ptr1);
*ptr2 = 20;
cout << *ptr1 << endl;
작업이 중단된 이유를 살펴보겠습니다.auto_ptr
소스 코드
복사 구성이 들어오는 개체를 호출하는 것을 볼 수 있습니다.release
방법, 이 방법은 다음과 같습니다_Right
가리키는 리소스가 새 리소스로 반환됩니다.auto_ptr
객체이면서 동시에_Right
(오래된auto_ptr
)의_Myptr
로 설정nullptr
한마디로 낡은 것을 놓는 것이다.auto_ptr
지정된 리소스는 새로운 리소스에 제공됩니다.auto_ptr
개최, 이전 것은 다음과 같이 설정됩니다.nullptr
.따라서 위의 코드는*ptr1
그냥 틀렸어.(auto_ptr
항상 마지막 스마트 포인터가 리소스를 관리하도록 하세요)
그래서,auto_ptr
컨테이너에서 사용할 수 있나요? 다음 코드를 참조하세요.
int main()
{
vector<auto_ptr<int>> vec;
vec.push_back(auto_ptr<int>(new int(10)));
vec.push_back(auto_ptr<int>(new int(20)));
vec.push_back(auto_ptr<int>(new int(30)));
cout << *vec[0] << endl; // 10
vector<auto_ptr<int>> vec2 = vec;
/* 这里由于上面做了vector容器的拷贝,
相当于容器中的每一个元素都进行了拷贝构造,
原来vec中的智能指针全部为nullptr了,
再次访问就成访问空指针了,程序崩溃
*/
cout << *vec[0] << endl; // 程序崩溃
return 0;
}
따라서 C++에서는 더 이상 사용되지 않습니다.auto_ptr
, 애플리케이션 시나리오가 매우 간단하지 않은 경우.그리고auto_ptr
C++11에서는 더 이상 사용되지 않으며 C++17에서는 완전히 제거되었습니다.
요약하다:auto_ptr
스마트 포인터에는 참조 카운팅 기능이 없으므로 이전 포인터를 직접 복사하여 얕은 복사 문제를 해결합니다.auto_ptr
로 설정되어 있습니다nullptr
, 마지막 것만 보자auto_ptr
자원을 보유하다
헤더 파일이 포함된 Boost 라이브러리를 설치해야 합니다.#include <boost/scoped_ptr.hpp>
사용할 준비가
구경하다scoped_ptr
소스 코드:
template<class T> class scoped_ptr
{
private:
scoped_ptr(scoped_ptr const&);
scoped_ptr& operator=(scoped_ptr const&);
...
};
볼 수 있다scoped_ptr
복사 생성자와 할당 함수가 모두 사유화되어 객체가 이 두 가지 작업을 지원하지 않고 호출할 수 없으므로 근본적으로 얕은 복사본의 발생을 방지합니다.
그래서scoped_ptr
컨테이너에서는 사용할 수 없습니다. 컨테이너가 서로 값을 복사하거나 할당하면scoped_ptr
객체 복사 생성자 및 할당 함수, 컴파일 오류
scoped_ptr
그리고auto_ptr
차이점: 소유권으로 설명할 수 있으며,auto_ptr
자원의 소유권은 마음대로 이전될 수 있지만,scoped_ptr
소유권이 이전되지 않습니다(복사 생성자 및 할당 기능이 비활성화되어 있기 때문).
scoped_ptr
일반적으로 사용되지 않음
먼저 살펴보세요unique_ptr
소스 코드의 일부:
template<class _Ty, class _Dx>
class unique_ptr
{
public:
/*提供了右值引用的拷贝构造函数*/
unique_ptr(unique_ptr&& _Right) { ... }
/*提供了右值引用的operator=赋值重载函数*/
unique_ptr& operator=(unique_ptr&& _Right) { ... }
/*
删除了unique_ptr的拷贝构造和赋值函数,
因此不能做unique_ptr智能指针对象的拷贝构造和赋值,
防止浅拷贝的发生
*/
unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;
};
위에서 보면,unique_ptr
조금scoped_ptr
동일한 작업이 수행됩니다. 즉, 복사 구성 및 할당 오버로드 기능이 비활성화되고 사용자는 사용이 금지됩니다.unique_ptr
스마트 포인터 얕은 복사 문제가 발생하지 않도록 명시적인 복사 구성 및 할당을 수행합니다.
복사 생성자가 없기 때문에unique_ptr<int> p1(new int()); unique_ptr<int> p2(p1);
당연히 틀린말이지
하지만unique_ptr
rvalue 참조 매개변수를 사용하여 복사 생성자와 할당 함수를 제공합니다. 즉,unique_ptr
스마트 포인터는 rvalue 참조를 통해 복사 구성 및 할당 작업을 수행하거나 생성할 수 있습니다.unique_ptr
임시 물건을 보관하는 장소.unique_ptr
함수의 반환 값으로 샘플 코드는 다음과 같습니다.
// 示例1
unique_ptr<int> p1(new int());
unique_ptr<int> p2(move(p1)); // 使用了右值引用的拷贝构造
p2 = move(p1); // 使用了右值引用的operator=赋值重载函数
// 示例2
unique_ptr<int> test_uniqueptr()
{
unique_ptr<int> ptr(new int());
return ptr;
}
int main()
{
/*
此处调用test_uniqueptr函数,在return ptr代码处,
调用右值引用的拷贝构造和赋值函数
*/
unique_ptr<int> p = test_uniqueptr(); // 调用带右值引用的拷贝构造函数
p = test_uniqueptr(); // 调用带右值引用的operator=赋值重载函数
return 0;
}
그런 다음 사용unique_ptr
이점은 사용자가 다음과 같은 것을 사용할 수 있다는 것입니다.unique_ptr<int> p2(move(p1));
진술을 보면 확실히 알 수 있다.p1
리소스를 다음으로 이전했습니다.p2
,p1
사용하시면 더 이상 자원이 보유되지 않습니다.auto_ptr
명시적으로는 아닐 것이다move
작성하면 의도가 명확하지 않습니다. 기본 레이어를 이해하지 못하면 잘못 사용하게 됩니다.
동시에,unique_ptr
이름에서 알 수 있듯이 결국 리소스를 참조하는 스마트 포인터는 하나만 있을 수 있으므로 참조 카운팅 없이 스마트 포인터를 사용하는 것이 우선적으로 권장됩니다.unique_ptr
참조 카운팅 기능을 갖춘 스마트 포인터에는 주로 다음이 포함됩니다.shared_ptr
그리고weak_ptr
, 참조 카운팅 포함혜택즉, 여러 스마트 포인터가 동일한 리소스를 관리할 수 있습니다. 그렇다면 참조 카운팅 기능이 있는 스마트 포인터란 무엇입니까?
참조 카운팅 포함: 각 객체의 리소스에 대한 참조 횟수를 일치시킵니다.
여러 스마트 포인터가 동일한 리소스를 가리키도록 허용되면 각 스마트 포인터는 리소스의 참조 카운트에 1을 추가합니다. 스마트 포인터가 소멸되면 리소스의 참조 카운트도 1씩 감소합니다. 마지막 스마트 포인터는 리소스의 참조 카운트가 1에서 0으로 감소하면 리소스를 해제할 수 있음을 의미합니다. 이것이 리소스 해제를 처리하는 개념입니다.
이전에 구현된 것을 가져옵니다.CSmartPtr
수정하고 코드를 직접 업로드하세요.
// 对资源进行引用计数的类
template<typename T>
class RefCnt
{
public:
RefCnt(T* ptr = nullptr) : mptr(ptr)
{
if (mptr != nullptr)
mcount = 1;
}
void addRef() { mcount++; } // 增加资源的引用计数
int delRef() { return --mcount; }
private:
T* mptr;
int mcount;
};
template<typename T>
class CSmartPtr
{
public:
CSmartPtr(T* ptr = nullptr) : mptr(ptr)
{
// 智能指针构造的时候给资源建立引用计数对象
mpRefCnt = new RefCnt<T>(mptr);
}
~CSmartPtr()
{
if (0 == mpRefCnt->delRef())
{
delete mptr;
mptr = nullptr;
}
}
// 实现拷贝构造
CSmartPtr(const CSmartPtr<T>& src)
:mptr(src.mptr), mpRefCnt(src.mpRefCnt)
{
if (mptr != nullptr)
mpRefCnt->addRef();
}
CSmartPtr<T>& operator=(const CSmartPtr<T>& src)
{
// 防止自赋值
if (this == &src)
return *this;
// 本身指向的资源减1
// 如果减1为0释放资源;如果减1不为0直接走
if (0 == mpRefCnt->delRef()) { delete mptr; }
mptr = src.mptr;
mpRefCnt = src.mpRefCnt;
mpRefCnt->addRef();
return *this;
}
T& operator*() { return *mptr; }
T* operator->() { return mptr; }
private:
T* mptr; // 指向资源的指针
RefCnt<T>* mpRefCnt; // 指向该资源引用计数对象的指针
};
// 那么现在就不会报错了,不会对同一个资源释放多次
CSmartPtr<int> ptr1(new int());
CSmartPtr<int> ptr2(ptr1);
CSmartPtr<int> ptr3;
ptr3 = ptr2;
*ptr1 = 20;
cout << *ptr2 << " " << *ptr3 << endl; // 20 20
이를 통해 여러 스마트 포인터가 동일한 리소스를 관리할 수 있습니다.
그러나 현재 구현하는 스마트 포인터는 스레드로부터 안전하지 않으며 다중 스레드 시나리오에서 사용할 수 없습니다.카레에 의해 구현됨shared_ptr
그리고weak_ptr
스레드로부터 안전합니다!
shared_ptr
:강한스마트 포인터(리소스의 참조 횟수를 변경할 수 있음)weak_ptr
:약한스마트 포인터(리소스의 참조 횟수를 변경하지 않음)이전 섹션에서 구현한 내용CSmartPtr
또한 강력한 스마트 포인터(리소스의 참조 횟수를 변경할 수 있음)
이런 식으로 이해될 수 있다: 약한 스마트 포인터 관찰하다 강력한 스마트 포인터 강력한 스마트 포인터관찰하다 리소스(메모리)
그래서,강력한 스마트 포인터 상호 참조(순환 참조) 문제 그럼 그게 뭐야?와서 구경해 보세요
class B; // 前置声明类B
class A
{
public:
A() { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
shared_ptr<B> _ptrb; // 指向B对象的智能指针
};
class B
{
public:
B() { cout << "B()" << endl; }
~B() { cout << "~B()" << endl; }
shared_ptr<A> _ptra; // 指向A对象的智能指针
};
int main()
{
shared_ptr<A> pa(new A()); // pa指向A对象,A的引用计数为1
shared_ptr<B> pb(new B()); // pb指向B对象,B的引用计数为1
pa->_ptrb = pb; // A对象的成员变量_ptrb也指向B对象,B的引用计数为2
pb->_ptra = pa; // B对象的成员变量_ptra也指向A对象,A的引用计数为2
cout << pa.use_count() << endl;
cout << pb.use_count() << endl;
return 0;
}
작업 결과:
A()
B()
2
2
보시다시피 A와 B는 파괴되지 않습니다. 즉, 상호 참조로 인해new
나오는 리소스를 해제할 수 없어 리소스 누출이 발생합니다!
분석하다:
밖으로main
기능 범위,pa
그리고pb
두 개의 로컬 객체가 소멸되고 객체 A와 객체 B의 참조 카운트가 각각 2에서 1로 감소합니다. A와 B를 해제하는 조건을 충족할 수 없습니다(해제 조건은 A와 B의 참조 카운트를 줄이는 것입니다). 0으로), 따라서 두 가지 원인이 됩니다.new
나온 A, B 객체를 해제할 수 없어 메모리 누수가 발생하는 문제입니다.강력한 스마트 포인터 상호 참조(순환 참조) 문제
해결책: 객체를 정의할 때는 강력한 스마트 포인터를 사용하고, 객체를 참조할 때는 약한 스마트 포인터를 사용하십시오.
class B; // 前置声明类B
class A
{
public:
A() { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
weak_ptr<B> _ptrb; // 指向B对象的弱智能指针(引用对象时,用弱智能指针)
};
class B
{
public:
B() { cout << "B()" << endl; }
~B() { cout << "~B()" << endl; }
weak_ptr<A> _ptra; // 指向A对象的弱智能指针(引用对象时,用弱智能指针)
};
int main()
{
// 定义对象时,用强智能指针
shared_ptr<A> pa(new A()); // pa指向A对象,A的引用计数为1
shared_ptr<B> pb(new B()); // pb指向B对象,B的引用计数为1
// A对象的成员变量_ptrb也指向B对象,B的引用计数为1,因为是弱智能指针,引用计数没有改变
pa->_ptrb = pb;
// B对象的成员变量_ptra也指向A对象,A的引用计数为1,因为是弱智能指针,引用计数没有改变
pb->_ptra = pa;
cout << pa.use_count() << endl; // 1
cout << pb.use_count() << endl; // 1
return 0;
}
작업 결과:
A()
B()
1
1
~B()
~A()
볼 수 있다, 밖으로main
기능 범위,pa
그리고pb
두 개의 로컬 객체가 소멸되고 객체 A와 객체 B의 참조 카운트가 각각 1에서 0으로 감소하여 A와 B를 해제하는 조건에 도달합니다.new
나온 A 개체와 B 개체가 소멸되어 문제가 해결되었습니다.강력한 스마트 포인터 상호 참조(순환 참조) 문제
볼 수 있다,weak_ptr
약한 스마트 포인터는 리소스의 참조 카운트를 변경하지 않습니다. 즉, 약한 스마트 포인터는 개체가 살아 있는지(참조 카운트가 0인지 여부)만 관찰하고 리소스를 사용할 수 없음을 의미합니다.
그런 다음 이때 클래스 A를 추가하면void testA() { cout << "非常好用的方法!!" << endl; }
이러한 방법은 B에 추가됩니다.void func() { _ptra->testA(); }
이 메소드를 호출해도 괜찮나요?
*
그리고->
연산자의 오버로드된 함수는 원시 포인터와 유사한 함수를 사용할 수 없습니다.해결책:
class B; // 前置声明类B
class A
{
public:
A() { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
void testA() { cout << "非常好用的方法!!" << endl; }
weak_ptr<B> _ptrb; // 指向B对象的弱智能指针(引用对象时,用弱智能指针)
};
class B
{
public:
B() { cout << "B()" << endl; }
~B() { cout << "~B()" << endl; }
void func()
{
shared_ptr<A> sp = _ptra.lock(); // 提升方法,出函数作用域就自动析构了
if (sp != nullptr)
sp->testA();
}
weak_ptr<A> _ptra; // 指向A对象的弱智能指针(引用对象时,用弱智能指针)
};
int main()
{
shared_ptr<A> pa(new A());
shared_ptr<B> pb(new B());
pa->_ptrb = pb;
pb->_ptra = pa;
cout << pa.use_count() << endl; // 1
cout << pb.use_count() << endl; // 1
pb->func();
return 0;
}
하지만
weak_ptr
객체를 소유하지 않지만 전달할 수 있음lock()
메소드는 객체에 대한 포인터를 얻으려고 시도합니다.shared_ptr
, 객체가 아직 살아 있는 경우(즉, 다른 객체가 있는 경우)shared_ptr
그것을 가리킨다),lock()
객체에 대한 포인터를 반환합니다shared_ptr
; 그렇지 않으면 빈 값이 반환됩니다.shared_ptr
사용
lock()
메소드에 대한 일반적인 시나리오는 임시 액세스가 필요한 경우입니다.weak_ptr
개체가 가리키는 동시에 개체의 수명 카운트를 늘리고 싶지 않습니다.
작업 결과:
A()
B()
1
1
非常好用的方法!!
~B()
~A()
이 시점에서 올바르게 호출되었음을 확인할 수 있습니다!
하나 보자여러 스레드에서 공유 객체에 액세스할 때 스레드 안전 문제: 스레드 A와 스레드 B가 공유 개체에 액세스합니다. 스레드 A가 개체를 소멸하는 경우 스레드 B는 이 시점에서 공유 개체의 멤버 메서드를 호출해야 하며 스레드 B는 개체 소멸을 완료하지 않습니다. 개체에 액세스하려고 하면 예기치 않은 오류가 발생합니다.
먼저 다음 코드를 살펴보세요.
class A
{
public:
A() { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
void testA() { cout << "非常好用的方法!!" << endl; }
};
// 子线程
void handler01(A* q)
{
// 睡眠两秒,此时main主线程已经把A对象给delete析构掉了
std::this_thread::sleep_for(std::chrono::seconds(2));
q->testA();
}
// main线程
int main()
{
A* p = new A();
thread t1(handler01, p);
delete p;
//阻塞当前线程,直到调用join()的线程结束
t1.join();
return 0;
}
실행 중q->testA();
이 성명이 발표되면,main
공유 객체가 스레드에 의해 파괴되었습니다. 이는 명백히 비합리적입니다.
합격하고 싶다면q
포인터가 A 개체에 액세스하려면 A 개체가 살아 있는지 확인해야 합니다. A 개체가 살아 있으면 다음을 호출합니다.testA
메서드에는 문제가 없습니다. A 객체가 소멸된 경우 다음을 호출하세요.testA
문제가 있어요!즉 말하자면q
객체 A에 접근할 때 객체 A가 살아 있는지 감지해야 합니다. 이 문제를 해결하려면 강력한 스마트 포인터와 약한 스마트 포인터를 사용해야 합니다.
이 코드를 살펴보겠습니다:
// 子线程
void handler01(weak_ptr<A> wp)
{
// 睡眠两秒
std::this_thread::sleep_for(std::chrono::seconds(2));
shared_ptr<A> sp = wp.lock();
if (sp != nullptr)
sp->testA();
else
cout << "A对象已经析构,不能再访问!" << endl;
}
// main线程
int main()
{
shared_ptr<A> p(new A());
thread t1(handler01, weak_ptr<A>(p));
t1.join();
return 0;
}
작업 결과:
A()
非常好用的方法!!
~A()
실행되고 있는 것을 볼 수 있습니다sp->testA();
,왜냐하면main
스레드가 호출되었습니다.t1.join()
이 때 메서드는 하위 스레드가 끝날 때까지 기다립니다.wp
통과하다lock
성공적으로 승격됨sp
위의 코드를 수정하세요:
// 子线程
void handler01(weak_ptr<A> wp)
{
// 睡眠两秒
std::this_thread::sleep_for(std::chrono::seconds(2));
shared_ptr<A> sp = wp.lock();
if (sp != nullptr)
sp->testA();
else
cout << "A对象已经析构,不能再访问!" << endl;
}
// main线程
int main()
{
{
shared_ptr<A> p(new A());
thread t1(handler01, weak_ptr<A>(p));
t1.detach();
}
std::this_thread::sleep_for(std::chrono::seconds(3));
return 0;
}
작업 결과:
A()
~A()
A对象已经析构,不能再访问!
보시다시피 범위를 설정하고t1
스레드를 분리하려면 스마트 포인터를 사용하세요.p
A가 범위를 벗어나 파괴되면 이때 인쇄됩니다.A对象已经析构,不能再访问!
, 그건wp
통과하다lock
다음으로 업그레이드하지 못했습니다.sp
위는 다중 스레드가 공유 객체에 액세스할 때 스레드 안전성 문제에 대한 참조입니다.shared_ptr
그리고weak_ptr
일반적인 응용 프로그램입니다.
참고 기사:C++ 스마트 포인터에 대한 깊은 이해