智能指针
概述:三种智能指针 shared_ptr \ unique_ptr \ weak_ptr
使用智能指针,首先需要包含memory头文件且智能指针都是在std命名空间中
#include <memory>
using namespace std;
shared_ptr
共享指针,用于多个指针指向同一个资源
基本用法
定义并初始化
shared_ptr<int>p;
p = make_shared<int>(100);
make_shared会动态分配一块内存,作用与new一样,但make_shared效率更高
shared_ptr<int>p {new int(100)};
shared_ptr<int>p {make_shared<int>(100)};
使用
class ball
{
public:
Ball(){
cout <<"A ball appears."<<endl;
}
~Ball(){
cout <<"A ball disappears."<<endl
}
void Bounce(){
cout <<"A ball jumps."<<endl;
}
};
int main()
{
shared_ptr<Ball>p make_shared<Ball>();
cout <<p.use_count()<<endl; //输出1
shared_ptr<Ball>p2 = p;
cout <<p.use_count()<< " " <<p2.use_count()<endl;//输出2 2
shared_ptr<Ball>p3 = p;
cout <<p.use_count()<<" "<<p2.use_count()<<" "
<<p3.use_count()<endl;//输出3 3 3
p.reset();
p2.reset();
p3.reset();//输出A ball disappears.
}
shared_ptr采用 ”引用计数“ 的方式管理,
每增加一个shared_ptr指向同一个对象时,记录引用计数+1,
当某个共享指针 reset 或者销毁的时候,引用计数-1,
引用计数为0,程序会自动释放对象
BTW
当某个函数接受的参数只能是裸指针,可以使用shared_ptr的get()方法来获取裸指针指向当前资源
当所有shared_ptr被销毁,但裸指针仍存在时,该指针所指向的内存仍然释放!再操作裸指针可能造成严重后果
Ball* rp = p.get();
避免裸指针与共享指针共用。
shared_ptr进阶用法
void close file(FILE*fp){
if (fp =nullptr)return;
fclose(fp);
cout <<"File closed."<<endl;
}
int main(
FILE*fp fopen("data.txt","w");
//引用计数为0 执行close_file()关闭文件而不是delete指针
shared_ptr<FILE>sfp {fp, close_file};
if (sfp == nullptr)
cerr <<"Error opening file."<<endl;
else cout <<"File opened." <<endl;
}
reset接受参数
shared_ptr<Ball>sp;
sp = make_shared<Ball>();
sp.reset(new Ball);//sp不再指向旧ball,引用计数-1,sp指向新的ball
访问类内成员变量,访问的是某个实例的成员,但是不希望使用该成员时实例本身被删除
struct Bar{ int i = 123;}
struct Foo{ Bar bar;}
int main(){
shared_ptr<Foo> f = make_shared<Foo>();
cout << f.use_count()<<endl; //prints 1
shared_ptr<Bar>b (f, &(f->bar));
cout << f.use_count() << endl; //prints 2
cout << b->i << endl; //prints 123
}
注意使用智能指针时,不要手动delete
使用shared_ptr 需要额外的性能开销(引用计数)
unique_ptr
独享指针,独占某个资源的管理权
没有额外的性能开销
基本用法
#include <memory>
unique_ptr<int>UP = make_unique<int>(100);//申请
UP.reset();//释放
不支持复制操作!
class ball
{
public:
Ball(){
cout <<"A ball appears."<<endl;
}
~Ball(){
cout <<"A ball disappears."<<endl
}
void Bounce(){
cout <<"A ball jumps."<<endl;
}
};
#include <memory>
#include <iostream>
using namespace std;
void func()
{
cout <<"Now in func."<<endl;
unique_ptr<Ball>up {make_unique<Ball>()}
up->Bounce();
cout <<"Before quiting func."<<endl;
}
int main(){
cout <<"Now in main."<<endl;
func();
cout <<"End Executing func."<<endl;
}
/*执行结果
Now in main.
Now in func.
A ball appears.
A ball jumps.
Before quiting func.
A ball disappears. //func执行结束后,up被销毁,执行析构,内存释放
End Executingfunc.
*/
unique_ptr被销毁时,其绑定的资源会自动释放
与普通指针一样的用法
auto ball = make_unique<Ball>();
ball->Bounce();
(*ball).Bounce();
与shared_ptr一样的用法
Ball*p = up->get();//获得资源的裸指针
up.reset();//释放资源 , up = nullptr
up.reset(new Ball{});//释放资源 ,并指向新的实例
其他用法
//1
Ball*ball = up.release();//解绑 返回资源的裸指针
delete ball;//
ball = nullptr;
//2
//up is a unique_ptr
up=nullptr;//会释放资源
不支持普通拷贝
unique_ptr<int>up1 = make_unique<int>(100);
unique_ptr<int> up2(up1);//error
up2 = up1;//error
//正确用法
unique_ptr<int>up1 = make_unique<int>(100);
unique_ptr<int>up2 (up1.release());//返回裸指针给up2初始化
unique_ptr<int>up2 = std::move(up1);//转移控制权
自定义分配函数 和释放函数
int*my_alloc(int v){
cout <<"Allocating "<<v <<endl;
return new int{v};
}
void my_dealloc(int *p){
cout <<"Deallocating "<<*p <endl;
delete p;
}
int main(){
//需要在模板参数里面,声明自定义释放函数的类型
unique_ptr<int, decltype(&my_dealloc)> cup {my_alloc(100), my_dealloc};
}
运行结果
Allocating 100
Deallocating 100
unique_ptr绑定释放函数是在编译期,避免了运行时绑定的时间损耗,删除函数的类型本身变成于unique_ptr实例的一部分
shared_ptr的自定义释放函数在运行时绑定
unique_ptr进阶用法
如何作为参数传递给函数
//错误写法
void pass_up(unique_ptr<int>up){
cout <<"In pass_up: "<<*up <<endl;
}
void main(){
auto up = make_unique<int>(123);
pass_up(up);//编译错误
}
//正确写法1 直接传资源
void pass_upl(int& value){
cout <<"In pass_upl:" <<value <<endl;
}
void main(){
auto up = make_unique<int>(123);
pass_up1(*up);//传递unique_ptr底下的资源
}
//正确写法2 传裸指针
void pass_up2(int*p){
cout << "In pass_up2:" << *p << endl;
}
void main(){
auto up = make_unique<int>(123);
pass_up2(up.get());
}
//正确写法3 传引用
void pass_up3(unique_ptr<int>&up){
cout << "In pass_up3:"<< *up <<endl;
up.reset();
}
void main(){
auto up = make_unique<int>(123);
pass_up3(up);
if (up == nullptr)
cout <<"up is reset."<<endl;
}
//正确写法4 move转移控制权
void pass_up4(unique_ptr<int>up){
cout <<"In pass_up4:"<< *up <<endl;
}
void main(){
auto up = make_unique<int>(123);
pass_up4(move(up));//转移资源控制权
if (up == nullptr)
cout <<"up is moved."<<endl;
}
作为返回值
unique_ptr<int> return_uptr(int value){
unique_ptr<int>up = make_unique<int>(value);
return up; // 或者return move(up);
}
void main(){
unique_ptr<int>up = return_uptr(321);
cout << "up:" << *up << endl;
}
weak_ptr
weak_ptr是shared_ptr的伴侣
对资源的引用是非拥有式的,没有资源的管理权限,不能控制资源的释放
访问资源的时候需要创建临时shared_ptr
可以检查资源是否存在
为什么需要使用weak_ptr?
shared_ptr 在环形依赖的时候,仍造成内存泄漏
//环形依赖的情况
#include <iostream>
#include <memory>
#include <string>
using namespace std;
struct School;
struct Teacher{
string name;
int age;
shared_ptr<School> school;
~Teacher(){
cout <<"Teacher Destructed."<<endl;
}
};
struct School{
string name;
shared_ptr<Teacher> principal;//校长
~School(){
cout <<"School Destructed."<<endl;
}
};
int main()
{
auto principle = make_shared<Teacher>();
auto university = make_shared<School>();
principal->school = university;
university->principal = principal;
return 0;
}
//程序执行完成,不会调用析构
基本用法
#include <iostream>
#include <memory>
using namespace std;
int main()
{
weak_ptr<int>wp;
shared_ptr<int> sp = make_shared<int>(100);
wp=sp;
//使用weak_ptr访问对象
auto resource = wp.lock() ;//资源未释放返回shared_ptr,资源已释放返回nullptr
if (resource){
cout <<"Number is "<< *resource <<endl;
}else{
cout <"wp is expired"<endl;
}
}
另一种用法
#include <iostream>
#include <memory>
using namespace std;
std::weak_ptr<int>gw;
void observe()
{
std:cout <<"gw.use_count()=="<< gw.use_count() <<";" ;
//.we have to make a copy of shared pointer before usage:
if (std:shared_ptr<int>spt = gw.lock())
std:cout <<"*spt =="<<*spt <<'\n';
else
std:cout <<"gw is expired\n";
}
int main()
{
{
auto sp = std::make_shared<int>(42);
gw = sp;
observe();
}
observe();
}
//编译输出
//gw.use_count()==1; *spt ==42
//gw.use_count()==0; gw is expired
环形依赖的解决方法,
struct Teacher{
string name;
int age;
weak_ptr<School> school;//改为weak_ptr即可
~Teacher(){
cout <<"Teacher Destructed."<<endl;
}
};
将School和Teacher类中的指针全部改为weak_ptr类型也行
shared_ptr是拥有一个物体的控制权,而weak_ptr只是一个观察者。如果你只需要观察物体而不是控制物体的分配、删除,那么就用weak_ptr,反之如果你想要拥有物体的控制权,用shared_ptr
注释:
上述资料为个人学习总结笔记,学习资源来自 b站 HexUp