智能指针详解

智能指针

概述:三种智能指针 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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值