前言
shared_ptr又称计数指针或共享指针,与unique_ptr不同的是它是可以共享数据的。shared_ptr原理:shared_ptr创建了一个计数器与类对象所指的内存相关联,copy则计数器加1,销毁则计数器减1,api为use_count()。
一、常用类型的shared_ptr
先来看看代码:
#include<iostream>
#include<memory>
#include"cat.h"
using namespace std;
int main(int argc, char *argv[])
{
//常量类型
std::shared_ptr<int> i_p_1=make_shared<int>(10);
//std::shared_ptr<int> i_p_1=make_shared<int>{new int(10)};
cout<<"value : "<<*i_p_1<<endl;
cout<<"use count:"<<i_p_1.use_count()<<endl;
//copy
std::shared_ptr<int> i_p_2=i_p_1;
cout<<"i_p_1 use count:"<<i_p_1.use_count()<<endl;
cout<<"i_p_2 use count:"<<i_p_2.use_count()<<endl;
//change
*i_p_2=30;
cout<<"i_p_1 value:"<<*i_p_1<<endl;
cout<<"i_p_2 value:"<<*i_p_2<<endl;
i_p_2=nullptr;
cout<<"i_p_1 use count:"<<i_p_1.use_count()<<endl;
cout<<"i_p_2 use count:"<<i_p_2.use_count()<<endl;
cout<<"----- yz -----"<<endl;
return 0;
}
执行结果:
上述代码中,如果将i_p_1置空会怎么样?
#include<iostream>
#include<memory>
#include"cat.h"
using namespace std;
int main(int argc, char *argv[])
{
//常量类型
std::shared_ptr<int> i_p_1=make_shared<int>(10);
//std::shared_ptr<int> i_p_1=make_shared<int>{new int(10)};
cout<<"value : "<<*i_p_1<<endl;
cout<<"use count:"<<i_p_1.use_count()<<endl;
//copy
std::shared_ptr<int> i_p_2=i_p_1;
cout<<"i_p_1 use count:"<<i_p_1.use_count()<<endl;
cout<<"i_p_2 use count:"<<i_p_2.use_count()<<endl;
//change
*i_p_2=30;
cout<<"i_p_1 value:"<<*i_p_1<<endl;
cout<<"i_p_2 value:"<<*i_p_2<<endl;
//i_p_2=nullptr;
i_p_1=nullptr;
cout<<"i_p_1 use count:"<<i_p_1.use_count()<<endl;
cout<<"i_p_2 use count:"<<i_p_2.use_count()<<endl;
cout<<"----- yz -----"<<endl;
return 0;
}
执行结果:
结论:如果将i_p_1置空,i_p_2的引用计数器值由2变为1。
补充实验:再生成一个指向该对象的指针用于验证引用计数器。
#include<iostream>
#include<memory>
#include"cat.h"
using namespace std;
int main(int argc, char *argv[])
{
//常量类型
std::shared_ptr<int> i_p_1=make_shared<int>(10);
//std::shared_ptr<int> i_p_1=make_shared<int>{new int(10)};
cout<<"value : "<<*i_p_1<<endl;
cout<<"use count:"<<i_p_1.use_count()<<endl;
//copy
std::shared_ptr<int> i_p_2=i_p_1;
cout<<"i_p_1 use count:"<<i_p_1.use_count()<<endl;
cout<<"i_p_2 use count:"<<i_p_2.use_count()<<endl;
//change
*i_p_2=30;
cout<<"i_p_1 value:"<<*i_p_1<<endl;
cout<<"i_p_2 value:"<<*i_p_2<<endl;
//i_p_2=nullptr;
i_p_1=nullptr;
std::shared_ptr<int> i_p_3=i_p_1;
cout<<"i_p_1 use count:"<<i_p_1.use_count()<<endl;
cout<<"i_p_2 use count:"<<i_p_2.use_count()<<endl;
cout<<"i_p_3 use count:"<<i_p_3.use_count()<<endl;
cout<<"----- yz -----"<<endl;
return 0;
}
执行结果:
结论:无论多少个计数指针指向nullptr,nullptr的引用计数值都为0。
实验:让i_p_3指向i_p_1,然后将i_p_1置空
#include<iostream>
#include<memory>
#include"cat.h"
using namespace std;
int main(int argc, char *argv[])
{
//常量类型
std::shared_ptr<int> i_p_1=make_shared<int>(10);
//std::shared_ptr<int> i_p_1=make_shared<int>{new int(10)};
cout<<"value : "<<*i_p_1<<endl;
cout<<"use count:"<<i_p_1.use_count()<<endl;
//copy
std::shared_ptr<int> i_p_2=i_p_1;
cout<<"i_p_1 use count:"<<i_p_1.use_count()<<endl;
cout<<"i_p_2 use count:"<<i_p_2.use_count()<<endl;
//change
*i_p_2=30;
cout<<"i_p_1 value:"<<*i_p_1<<endl;
cout<<"i_p_2 value:"<<*i_p_2<<endl;
//i_p_2=nullptr;
std::shared_ptr<int> i_p_3=i_p_1;
i_p_1=nullptr;
cout<<"i_p_1 use count:"<<i_p_1.use_count()<<endl;
cout<<"i_p_2 use count:"<<i_p_2.use_count()<<endl;
cout<<"i_p_3 use count:"<<i_p_3.use_count()<<endl;
cout<<"----- yz -----"<<endl;
return 0;
}
执行结果:
结果分析:i_p_2和i_p_3的引用计数值相同。
二、自定义类型的shared_ptr
对自定义的cat类使用shared_ptr:
#include<iostream>
#include<memory>
#include"cat.h"
using namespace std;
int main(int argc, char *argv[])
{
//自定义变量
std::shared_ptr<Cat> c_p_1=make_shared<Cat>();
cout<<"c_p_1 use cout : "<<c_p_1.use_count()<<endl;
std::shared_ptr<Cat> c_p_2=c_p_1;
std::shared_ptr<Cat> c_p_3=c_p_1;
cout<<"c_p_1 use cout : "<<c_p_1.use_count()<<endl;
cout<<"c_p_2 use cout : "<<c_p_2.use_count()<<endl;
cout<<"c_p_3 use cout : "<<c_p_3.use_count()<<endl;
cout<<"----- yz -----"<<endl;
return 0;
}
实验结果:
修改其中一个值会怎样?
std::shared_ptr<Cat> c_p_1=make_shared<Cat>();
cout<<"c_p_1 use cout : "<<c_p_1.use_count()<<endl;
std::shared_ptr<Cat> c_p_2=c_p_1;
std::shared_ptr<Cat> c_p_3=c_p_1;
cout<<"c_p_1 use cout : "<<c_p_1.use_count()<<endl;
cout<<"c_p_2 use cout : "<<c_p_2.use_count()<<endl;
cout<<"c_p_3 use cout : "<<c_p_3.use_count()<<endl;
c_p_1.reset();//在这个清空c_p_1
cout<<"c_p_1 use cout : "<<c_p_1.use_count()<<endl;
cout<<"c_p_2 use cout : "<<c_p_2.use_count()<<endl;
cout<<"c_p_3 use cout : "<<c_p_3.use_count()<<endl;
cout<<"----- yz -----"<<endl;
实验结果:
结论:无论有多少个计数指针指向同一个对象,该对象都只销毁一次。
三、shared_ptr与函数调用
1.pass by value
使用copy语义,智能指针内部计数器加一。
#include<iostream>
#include<memory>
#include"cat.h"
using namespace std;
void cat_by_value(std::shared_ptr<Cat> cat)
{
cout<<cat->get_name()<<endl;
cout<<"func use count:"<<cat.use_count()<<endl;
}
int main(int argc, char *argv[])
{
std:shared_ptr<Cat> c1=make_shared<Cat>("dd");
cat_by_value(c1);
c1->cat_info();
cout<<"c1 use count :"<<c1.use_count()<<endl;
cout<<"----- yz -----"<<endl;
return 0;
}
实验结果:
结论:函数调用完成后销毁的是指针而不是指针指向的内存。
实验:在函数调用内部修改成员变量:
#include<iostream>
#include<memory>
#include"cat.h"
using namespace std;
void cat_by_value(std::shared_ptr<Cat> cat)
{
cout<<cat->get_name()<<endl;
cat->set_cat_name("ee");
cout<<"func use count:"<<cat.use_count()<<endl;
}
int main(int argc, char *argv[])
{
std:shared_ptr<Cat> c1=make_shared<Cat>("dd");
cat_by_value(c1);
c1->cat_info();
cout<<"c1 use count :"<<c1.use_count()<<endl;
cout<<"----- yz -----"<<endl;
return 0;
}
实验结果:
实验结论:如果在函数调用里边修改了对象里边的成员,那么这个修改是持久的
2 .pass by ref
实验:
#include<iostream>
#include<memory>
#include"cat.h"
using namespace std;
void cat_by_value(std::shared_ptr<Cat> cat)
{
cout<<cat->get_name()<<endl;
cat->set_cat_name("ee");
cout<<"func use count:"<<cat.use_count()<<endl;
}
void cat_by_ref(std::shared_ptr<Cat> &cat)
{
cout<<cat->get_name()<<endl;
cat.reset(new Cat());
cout<<"func use count:"<<cat.use_count()<<endl;
}
int main(int argc, char *argv[])
{
std:shared_ptr<Cat> c1=make_shared<Cat>("dd");
cat_by_value(c1);
c1->cat_info();
cout<<"c1 use count :"<<c1.use_count()<<endl;
cat_by_ref(c1);
c1->cat_info();
cout<<"----- yz -----"<<endl;
return 0;
}
实验结果:
结论:从“ee"的下一行输出我们可以知道,当引用计数器为0时,对象自动销毁。
3.链式调用
#include<iostream>
#include<memory>
#include"cat.h"
using namespace std;
void cat_by_ref(const std::shared_ptr<Cat> &cat)
{
cout<<cat->get_name()<<endl;
//cat.reset(new Cat());
cout<<"func use count:"<<cat.use_count()<<endl;
}
std::shared_ptr<Cat> get_shared_ptr()
{
std::shared_ptr<Cat> cat_p=std::make_shared<Cat>("local cat");
return cat_p;
}
int main(int argc, char *argv[])
{
std:shared_ptr<Cat> c1=make_shared<Cat>("dd");
c1->cat_info();
cout<<"c1 use count :"<<c1.use_count()<<endl;
cat_by_ref(c1);
c1->cat_info();
std::shared_ptr<Cat> c_p=get_shared_ptr();
c_p->cat_info();
c1=c_p;
cout<<"c_p use count:"<<c_p.use_count()<<endl;
cout<<"c1 use count:"<<c1.use_count()<<endl;
cout<<"----- yz -----"<<endl;
return 0;
}
实验结果
实验结论:当c1指向c_p时,c1原来指向的对象引用计数器变为0,原来的对象于是被销毁。
补充实验:
int main(int argc, char *argv[])
{
std::shared_ptr<Cat> c_p=get_shared_ptr();
c_p->cat_info();
get_shared_ptr();
get_shared_ptr();
cout<<"c_p use count:"<<c_p.use_count()<<endl;
cout<<"----- yz -----"<<endl;
return 0;
}
实验结果:
实验结论:
每次调用链式函数的时候,都会开辟一块新的内存,如果调用结果后,没有参数接收返回值,那么该内存对应的对象引用计数器减为0,于是对象自动销毁。
四、shared_ptr与unique_ptr
不能将shared_ptr转换为unique_ptr, 但通过std::move,unique_ptr可以转换为shared_ptr。将你的函数返回unique_ptr是一种常见的设计模式,这样可以提高代码的利用度,你可以随时改变为shared_ptr
1.unique_ptr转换为shared_ptr
#include<iostream>
#include<memory>
#include"cat.h"
using namespace std;
std::unique_ptr<Cat> get_unique_ptr()
{
std::unique_ptr<Cat> cat_p=std::make_unique<Cat>("local cat");
return cat_p;
}
int main(int argc, char *argv[])
{
std::unique_ptr<Cat> c_p_1=std::make_unique<Cat>("dd");
std::shared_ptr<Cat> c_p_2=std::move(c_p_1);
cout<<"c_p_2 use count:"<<c_p_2.use_count()<<endl;
cout<<"----- yz -----"<<endl;
return 0;
}
实验结果:
实验结论:unique_ptr能通过move语义转变为shared_ptr.
2.用shared_ptr接收unique_ptr
#include<iostream>
#include<memory>
#include"cat.h"
using namespace std;
std::unique_ptr<Cat> get_unique_ptr()
{
std::unique_ptr<Cat> cat_p=std::make_unique<Cat>("local cat");
return cat_p;
}
int main(int argc, char *argv[])
{
std::unique_ptr<Cat> c_p_1=std::make_unique<Cat>("dd");
std::shared_ptr<Cat> c_p_2=std::move(c_p_1);
cout<<"c_p_2 use count:"<<c_p_2.use_count()<<endl;
//func
std::shared_ptr c_p_3=get_unique_ptr();
if(c_p_3)
{
c_p_3->cat_info();
cout<<"c_p_3 use count: "<<c_p_3.use_count()<<endl;
}
cout<<"----- yz -----"<<endl;
return 0;
}
实验结果:
实验结论:unique_ptr能用shared_ptr来接收
3.不能用unique_ptr接收shared_ptr
4.不允许shared_ptr变量直接对unique_ptr变量进行接收。
5.shared_ptr变量可以用unique_ptr初始化
int main(int argc, char *argv[])
{
std::unique_ptr<Cat> c_p_1=std::make_unique<Cat>("dd");
std::shared_ptr<Cat> c_p_2=move(c_p_1);
cout<<"c_p_2 use count:"<<c_p_2.use_count()<<endl;
//func
std::shared_ptr c_p_3=std::make_unique<Cat>("ab");
if(c_p_3)
{
c_p_3->cat_info();
cout<<"c_p_3 use count: "<<c_p_3.use_count()<<endl;
}
cout<<"----- yz -----"<<endl;
return 0;
}
实验结果:
补充实验:
int main(int argc, char *argv[])
{
std::unique_ptr<Cat> c_p_1=std::make_unique<Cat>("dd");
std::shared_ptr<Cat> c_p_2=move(c_p_1);
cout<<"c_p_2 use count:"<<c_p_2.use_count()<<endl;
//func
std::shared_ptr c_p_3=std::make_unique<Cat>("ab");
if(c_p_3)
{
c_p_3->cat_info();
cout<<"c_p_3 use count: "<<c_p_3.use_count()<<endl;
}
c_p_2=c_p_3;
cout<<"c_p_3 use count: "<<c_p_3.use_count()<<endl;
cout<<"----- yz -----"<<endl;
return 0;
}
结论:用shared_ptr定义的变量一定是shared_ptr类型的。
总结
以上为shared_ptr的使用方法,如果有不足的地方,欢迎指出。