推荐:cplusplus
维护并共享对象的所有权,这个是shared_ptr 指针的特点。
一:shared_ptr 创建使用
常用的构造:
template <class U> explicit shared_ptr (U* p);
template <class U, class D> shared_ptr (U* p, D del);
del 是自定义不抛异常的deleter 回调函数。
这里shared_ptr 指针构造函数的样例:shared_ptr construct
以下是补充说明:
//for smart pointer
#include <iostream>
#include <memory>
using namespace std;
struct Test {
int m;
Test(int n=0):m(n){cout<<this<<" ,default construct\n";}
Test(const Test& obj):m(obj.m){cout<<this<<" ,copy construct\n";}
Test(Test &&obj):m(obj.m){cout<<this<<" ,move construct\n";}
~Test(){cout<<this<<" ,disconstruct\n";}
void showValue() {
cout<<"data in object:"<<m<<endl;
}
static void deleter(Test *p) noexcept {
cout<<"custom deleter,to delete: "<<p<<endl;
delete p;
}
};
void test_sharedptr(){
#if 0
{
Test obj;
//shared_ptr<Test> testobj1 (&obj,[&](Test *){});
shared_ptr<Test> testobj1 (&obj);
shared_ptr<Test> testobj2 = testobj1;
cout<<"testobj1 usecout:"<<testobj1.use_count()<<"\n";
cout<<" here is double free about local variable ,obj"<<endl;
}
#endif
{
cout<<"\nexample 2,heap obj :\n";
Test* obj2 =new Test;
shared_ptr<Test> testobj3 (obj2);
shared_ptr<Test> testobj4 = testobj3;
cout<<"testobj3 usecout:"<<testobj3.use_count()<<"\n";
}
{
cout<<"\nexample 3,set deleter example:"<<endl;
shared_ptr<Test> testobj5(new Test,Test::deleter);
shared_ptr<Test> testobj6 = testobj5;
cout<<"testobj5 usecout:"<<testobj5.use_count()<<"\n";
}
{
cout<<"\nexample 4,by make_shared:"<<endl;
shared_ptr<Test> testobj7 = make_shared<Test>(10);
testobj7->showValue();
}
}
int main()
{
test_sharedptr();
return 0;
}
结果:
example 2,heap obj :
0x55b5622c82c0 ,default construct
testobj3 usecout:2
0x55b5622c82c0 ,disconstruct
example 3,set deleter example:
0x55b5622c82e0 ,default construct
testobj5 usecout:2
custom deleter,to delete: 0x55b5622c82e0
0x55b5622c82e0 ,disconstruct
example 4,by make_shared:
0x55b5622c82f0 ,default construct
data in object:10
0x55b5622c82f0 ,disconstruct
要注意的点:
1:shared_ptr 管理的obj对象,必须不能是临时的变量,即,不可以是栈上的对象,否则会有double free的问题。
2:构造的原始指针,不能构造两个sp对象
int main() {
int * n= new int(10);
std::shared_ptr<int> test1 (n);
std::shared_ptr<int> test2 (n);
std::cout<<test1.use_count()<<" , cout :"<<test2.use_count()<<std::endl;
return 0;
}
double free.报错:
1 , cout :1
free(): double free detected in tcache 2
Aborted
3:enable_shared_from_this
struct Test {
int m;
/*...skip...*/
shared_ptr<Test> getSharedObj() {
return shared_ptr<Test>(this);
}
};
{
cout<<"\nexample 5,shared object:"<<endl;
shared_ptr<Test> testobj8 = make_shared<Test>(10);
shared_ptr<Test> testobj9 = testobj8->getSharedObj();
}
double free报错:
example 5,shared object:
0x55f61ea692f0 ,default construct
0x55f61ea692f0 ,disconstruct
double free or corruption (out)
我们改进下类的定义:
struct Test:public enable_shared_from_this<Test> {
int m;
Test(int n=0):m(n){cout<<this<<" ,default construct\n";}
Test(const Test& obj):m(obj.m){cout<<this<<" ,copy construct\n";}
Test(Test &&obj):m(obj.m){cout<<this<<" ,move construct\n";}
~Test(){cout<<this<<" ,disconstruct\n";}
void showValue() {
cout<<"data in object:"<<m<<endl;
}
static void deleter(Test *p) noexcept {
cout<<"custom deleter,to delete: "<<p<<endl;
delete p;
}
shared_ptr<Test> getSharedObj() {
// return shared_ptr<Test>(this);
return shared_from_this();
}
};
如果还有其他的点,请添加到评论,感谢。
二,weak_ptr
std::weak_ptr是C++11中引入的一种智能指针,它可以用来协助std::shared_ptr。weak_ptr的主要特征和使用场景如下:
- weak_ptr不控制对象的生命周期,它不会影响对象的引用计数。
- weak_ptr指向一个由shared_ptr管理的对象,可以通过weak_ptr的lock()函数获取共享对象的shared_ptr。
- 当被管理的对象销毁后,weak_ptr不会自动置空,需要检查lock()的返回值来判断对象是否已销毁。
- weak_ptr主要用来解决shared_ptr循环引用问题,避免内存泄漏。
- weak_ptr可以在类中保留对外部shared对象的引用,而不会延长对象生命周期。
std::shared_ptr<Object> obj = std::make_shared<Object>();
std::weak_ptr<Object> weakObj(obj);
// 使用obj
if(std::shared_ptr<Object> obj2 = weakObj.lock()) {
// 使用obj2
} else {
// obj已销毁
}
上面代码中,weakObj不会延长obj的生命周期。通过lock检查obj是否已销毁后再使用。
如果要引用weak_ptr管理的对象指针,则需要通过lock 转换成share_ptr指针。
所以weak_ptr适合用来在不延长生命周期的情况下获取共享对象的指针
struct TestWeak2;
struct TestWeak3;
struct TestWeak1 {
shared_ptr<TestWeak2> mptr;
shared_ptr<TestWeak3> mptr3;
~TestWeak1(){cout<<__func__<<" TestWeak1 disconstruct\n"; }
};
struct TestWeak2{
shared_ptr<TestWeak1> mptr;
~TestWeak2(){cout<<__func__<<" TestWeak2 disconstruct\n"; }
};
struct TestWeak3{
weak_ptr<TestWeak1> mptr;
~TestWeak3(){cout<<__func__<<" TestWeak3 disconstruct\n"; }
};
void test_weakptr() {
{
shared_ptr<TestWeak1> testobj1= make_shared<TestWeak1>();
shared_ptr<TestWeak2> testobj2= make_shared<TestWeak2>();
testobj1->mptr = testobj2;
testobj2->mptr = testobj1;
cout<<"testobj1 cout: "<<testobj1.use_count()<<", testobj2 cout: "<<testobj2.use_count()<<endl;
}
cout<<"\nthe following test wake_ptr:"<<endl;
{
shared_ptr<TestWeak1> testobj1= make_shared<TestWeak1>();
shared_ptr<TestWeak3> testobj3= make_shared<TestWeak3>();
testobj1->mptr3 = testobj3;
testobj3->mptr = testobj1;
cout<<"testobj1 cout: "<<testobj1.use_count()<<", testobj3 cout: "<<testobj3.use_count()<<endl;
}
cout<<"exit test_weakptr"<<endl;
}
结果:
testobj1 cout: 2, testobj2 cout: 2
the following test wake_ptr:
testobj1 cout: 1, testobj3 cout: 2
~TestWeak1 TestWeak1 disconstruct
~TestWeak3 TestWeak3 disconstruct
exit test_weakptr
以上例子,列举了shared_ptr相互引用,导致在生命周期结束的时候,引用计数不为0,对象也不会被销毁,会引起内存泄漏。引入了weak_ptr, 即打破死循环。
三, unique_ptr
不共享所有权的智能指针。同一个时刻,一个object 只能被一个unique_ptr管理。可以借move移动操作,在不同的unique_ptr对象之间移动,也有另外一种称呼,叫转移所有权。
主要的构造形式:
explicit unique_ptr (pointer p) noexcept;
unique_ptr (unique_ptr&& x) noexcept;
所有的类型可查相关API: cplusplus
没有拷贝构造:
unique_ptr( const unique_ptr& ) = delete;
另外,不同shared_ptr, C++14之前的标准库里没有maked_unique. 据说是大佬忘记了,所以在C++14 的标准里加了这个:
template< class T, class... Args >
unique_ptr<T> make_unique( Args&&... args );
template<class T, class... Args>
std::enable_if_t<!std::is_array<T>::value, std::unique_ptr<T>>
make_unique(Args&&... args)
{
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
不能创建管理数组对象的unique_ptr.
同样的,shared_ptr ,对于c++11 里,make_shared 也是不能创建数组的。
//(since C++11) (T is not array)
template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args );
//(since C++20) (T is U[])
template< class T >
shared_ptr<T> make_shared( std::size_t N );
//(since C++20) (T is U[])
template< class T >
shared_ptr<T> make_shared( std::size_t N, const std::remove_extent_t<T>& u );
从官网上看,c++20 之后,提供了对数组的重载。
回归话题。
示例:
#include <iostream>
#include <memory>
class Foo {
public:
Foo() { std::cout << "Foo constructed" << std::endl; }
~Foo() { std::cout << "Foo destroyed" << std::endl; }
};
int main() {
// 创建一个Foo对象的unique_ptr
std::unique_ptr<Foo> ptr1(new Foo());
// 通过移动语义传递所有权
std::unique_ptr<Foo> ptr2 = std::move(ptr1);
// ptr2现在管理这个对象
// ptr1已置空
// ptr2离开作用域会自动释放对象
return 0;
}
智能指针的引入吗,最大的目的是防止内存泄漏。
对于unique_ptr这么特殊的指针,使用的场景,相对就很少。什么时候使用呢?适合使用unique_ptr:
1: 临时看管某个对象
2:重要的数据,就需要独占式。
3:具有一定的线程安全特性。
如果是线程资源,那么unique_ptr,独占式的特性,能保证绝对的线程安全。即使传给其他线程,只能通过move,移动语义来操作。
智能指针用起来,相对比较简单,接口也不多。
总结下注意点:
1:智能指针计数上是线程安全的,但是管理的对象不是线程安全的。所以,智能指针非线程安全。
2:智能指针管理的对象需要基于堆上创建。
3:shared_ptr 指针管理的类对象,类建议继承enable_shared_from_this 。
也是防止出现double free。