一、shared_ptr, weak_ptr和unique_ptr之间的赋值操作
1.shared_ptr之间的赋值操作
当使用shared_ptr对象进行赋值时,左值会指向新的对象,所以左值的引用计数-1,右值的引用计数+1,如果左值的引用计数为0,则会对象会被释放,调用析构函数
class test
{
public:
test():a(9){cout<<__func__<<endl;}
test(int a_){
cout<<"test(int a)"<<endl;
a=a_;
}
~test(){
cout<<__func__<<endl;
cout<<a<<endl;
}
int a;
};
void shareptrassign()
{
shared_ptr<test> sp1=make_shared<test>();
shared_ptr<test> sp2=make_shared<test>(10);
cout<<sp1.use_count()<<sp2.use_count()<<endl;
sp2=sp1;
cout<<sp1.use_count()<<sp2.use_count()<<endl;
}

上述代码中,当把sp1赋值给sp2时,sp2指向的新的对象,所以先前指向的对象引用计数减一至0,所以将对象释放,调用析构函数,之后,sp1和sp2都指向一个对象,所以引用计数都为2
2.unique_ptr之间的赋值操作
虽然不能直接使用unique_ptr对象给另一个unique_ptr对象赋值,但是可以使用std::move函数给unique_ptr对象重新赋值。
class test
{
public:
test():a(9){cout<<__func__<<endl;}
test(int a_){
cout<<"test(int a)"<<endl;
a=a_;
}
~test(){
cout<<__func__<<endl;
cout<<a<<endl;
}
int a;
};
void uniqueptrassign()
{
unique_ptr<test> sp1=make_unique<test>();
unique_ptr<test> sp2=make_unique<test>(10);
sp2=move(sp1);
cout<<"---------"<<endl;
cout<<sp1.get()<<endl;
cout<<sp2.get()<<endl;
cout<<sp2->a<<endl;
cout<<"---------"<<endl;
}

通过打印结果可知,当执行sp2=move(sp1);时,sp2指向sp1,同时释放sp2原来的指向的对象,而调用move(sp1)时,sp1将对象的控制权转移给sp2,自身不指向任何对象,所以sp1的指针值是0,之后的打印证明,sp2接管了原先sp1指向的对象
3.weak_ptr之间的赋值操作
因为weak_ptr是shared_ptr的附属品,只有绑定了shared_ptr后,weak_ptr的引用计数才会增加,之后weak_ptr和weak_ptr之间的赋值操作不会改变weak_ptr的引用计数,但是会改变weak_ptr的绑定对象
示例
void weakptrassign()
{
weak_ptr<test> wp1;
weak_ptr<test> wp2(wp1);
shared_ptr<test> sp=make_shared<test>();
shared_ptr<test> sp2=make_shared<test>(10);
wp1=sp;
wp2=sp2;
cout<<wp1.lock()->a<<endl;
cout<<wp2.lock()->a<<endl;
cout<<"---------"<<endl;
wp1=wp2;
cout<<wp1.use_count()<<endl;
cout<<wp2.use_count()<<endl;
cout<<wp1.lock()->a<<endl;
cout<<wp2.lock()->a<<endl;
}

可见,当对weak_ptr赋值后,weak_ptr对象本身的引用计数并没有增加,但是因为wp1=wp2;,所以wp1也和sp2绑定了,所以打印出的a值是10
此外上述代码先用默认构造函数创建一个对象并用sp指向,再创建sp2指向的另一个test对象,但是释放的顺序是先释放sp2指向的对象,再释放sp指向的对象,顺序正好相反
二、shared_ptr, weak_ptr和unique_ptr作为函数的返回值
1.shared_ptr作为函数的返回值
shared_ptr<test> returnvaltest()
{
cout<<__func__<<endl;
shared_ptr<test> sp=make_shared<test>(10);
return sp;
}
int main(int argc, char const *argv[])
{
shared_ptr<test> sp=returnvaltest();
cout<<sp.use_count()<<endl;
return 0;
}

上述代码的执行顺序是这样的:
调用函数returnvaltest,然后用make_shared创建一个test对象,调用test的构造函数,并用make_shared的返回值初始化智能指针sp并调用shared_ptr<test> 的拷贝构造函数。此时,test对象的引用计数为1。之后,sp返回时,创建一个shared_ptr<test>的临时对象同时用sp初始化该临时对象,又调用了shared_ptr<test> 的拷贝构造函数并将该临时对象返回,此时test对象的引用计数为2。同时,局部变量sp被销毁,调用shared_ptr<test>的析构函数,引用计数又为1。接着用返回的临时变量初始化主函数中的智能指针sp,调用了sp的拷贝构造函数,此时test对象的引用计数又为2。紧接着临时变量被销毁,test对象的引用计数为1并打印,主函数返回,sp被销毁,调用了sp的析构函数,同时堆上的test对象也被销毁,调用了test的析构函数,程序结束。上述过程较为复杂,只不过shared_ptr的实现没有打印
如果自己写一个类的话并关闭编译器的优化,就能比较好的看到上述的执行过程,关闭编译器的优化编译命令见博客https://blog.youkuaiyun.com/Master_Cui/article/details/106885137
test returnval()
{
cout<<__func__<<endl;
test t;
cout<<"---------------"<<endl;
return t;
}
int main(int argc, char const *argv[])
{
test t=returnval();
cout<<"---------------"<<endl;
return 0;
}

过程类似
所以,当shared_ptr作为返回值返回时,并不会让指向内存的引用计数增加,而是保持引用计数不变
2.unique_ptr作为函数的返回值
因为unique_ptr是独占资源的,所以不存在引用计数的问题,当一个函数返回unique_ptr时,可以用来给一个unique_ptr对象初始化,也可以用move函数给一个unique_ptr对象赋值
3.weak_ptr作为函数的返回值
最好不要使用weak_ptr作为函数的返回值,因为weak_ptr是和shared_ptr绑定的,当weak_ptr返回时,shared_ptr指向的对象也有可能已经被销毁,此时的weak_ptr也已经无效
weak_ptr<test> returnvaltest3()
{
cout<<__func__<<endl;
shared_ptr<test> sp=make_shared<test>();
weak_ptr<test> wp=sp;
cout<<wp.use_count()<<endl;
return wp;
}
int main(int argc, char const *argv[])
{
weak_ptr<test> wp=returnvaltest3();
cout<<wp.use_count()<<endl;
return 0;
}

主函数中,使用wp接受函数的返回值,但是函数返回后,局部变量shared_ptr<test> sp已经无效,所以wp.use_count()为0。
三、shared_ptr, weak_ptr和unique_ptr作为函数的形参
1.shared_ptr作为函数的形参
如果使用值传递,当传入shared_ptr时,会调用拷贝构造函数,实参会对形参进行初始化,引用计数+1
void sppara(shared_ptr<test> &sp)
{
cout<<sp.use_count()<<endl;
}
int main(int argc, char const *argv[])
{
shared_ptr<test> sp=make_shared<test>();
sppara(sp);
return 0;
}

但是如果是按引用传递,那么将智能指针本身传递进来,引用计数不变

所以,如果不想因为引用计数的原因导致对象的内存没有被释放,那么,当传递shared_ptr对象时,请按引用传递
2.unique_ptr作为函数的形参
unique_ptr不存在引用计数的问题,但是,因为unique_ptr的拷贝构造是delete的,所以当把一个unique_ptr对象传给一个函数时,不能直接传递,需要将unique_ptr放到move函数中
unique_ptr (const unique_ptr&) = delete;
void uppara(unique_ptr<test> up)
{
cout<<up->a<<endl;
}
int main(int argc, char const *argv[])
{
unique_ptr<test> up=make_unique<test>();
uppara(move(up));
return 0;
}

如果不想使用move函数,那么可以将函数的形参设置为unique_ptr的引用
void uppara(unique_ptr<test> &up)
{
cout<<up->a<<endl;
}
int main(int argc, char const *argv[])
{
unique_ptr<test> up=make_unique<test>();
uppara(up);
return 0;
}
输出结果同上
3.weak_ptr作为函数的形参
weak_ptr的情形比较简单,因为weak_ptr和shared_ptr绑定,所以,无论是传值还是传引用,都不会导致weak_ptr的引用计数增加
void wppara(weak_ptr<test> &wp)
{
cout<<wp.use_count()<<endl;
}
int main(int argc, char const *argv[])
{
shared_ptr<test> sp=make_shared<test>();
weak_ptr<test> wp=sp;
wppara(wp);
return 0;
}

虽然不会增加引用计数,但是weak_ptr的赋值或者拷贝会导致weak_ptr指向的对象发生改变
欢迎大家评论交流,作者水平有限,如有错误,欢迎指出
5665

被折叠的 条评论
为什么被折叠?



