前言
突然发现自己平时不怎么写博客,但是面试的时候面试官对写博客有好印象,所以从今天开始养成写博客的好习惯。昨天学了C++的智能指针,这在我之前的大华笔试的时候做到过,但是当时并没有学,仔细学了一下说一下我的思考
一、智能指针是什么?
C++中堆内存的对象是以指针的形式存在的,使用new来创建,使用delete来销毁。但是在实际的使用过程中很容易出现忘记调用delete的情况,从而造成内存泄露的问题。
诸如Java、C#等面向对象的语言直接提供垃圾回收机制来定期处理不再使用的对象,因此C++在98标准中引入了智能指针的概念,并在C++11中趋于完善。
使用智能指针,就可以让指针不需要调用delete。智能指针主要用于管理堆内存,它将普通的指针封装成一个栈对象。当栈对象的生命周期结束后,会在其析构函数中释放申请的内存,从而防止内存泄露。
C++中有四种智能指针:auto_ptr、unique_ptr、shared_ptr、weak_ptr。其中后三者是C++11中新增的,第一个是C++98增加的。
使用智能指针需要引入头文件 #include
二、智能指针的分类
1.auto_ptr
该指针是在C++98就已经存在,现在已经不推荐使用。
给智能指针赋值为 auto_ptr<类型名> 指针名 (类型的对象);
使用赋值之后的指针的内容用内置函数 .get()
如果要更换指针的指向对象用内置函数 .reset(类型的对象);
reset 更换对象之后,会在构造对象之后将更换之前的对象进行析构
最大的缺点是赋值拷贝会让出所有权
代码如下:
#include <iostream>
#include <memory>
using namespace std;
class Test
{
private:
string s;
public:
Test(string s):s(s)
{
cout << "构造函数" << endl;
}
void show()
{
cout << s << endl;
}
~Test()
{
cout << "析构函数" << endl;
}
};
int main()
{
// 局部代码块
{
// 智能指针ap1管理堆内存Test对象
auto_ptr<Test> ap1(new Test("A"));
// 先获取堆内存对象后再调用其成员
ap1.get()->show();
// 让智能指针换一个管理的对象,之前管理的对象会被销毁
ap1.reset(new Test("B"));
ap1.get()->show();
// 释放对象管理
// ap1.release(); 不会销毁管理的对象,仅仅是解除关系
}
cout << "主函数执行结束" << endl;
return 0;
}
2.unique_ptr
unique_ptr其实就是在auto_ptr的基础上对拷贝赋值进行了限制。
作为对auto_ptr的改进,unique_ptr对其持有的堆内存对象具有唯一拥有权,即unique_ptr不可以拷贝和赋值给其它对象,其拥有的堆内存对象仅自己独占。同样,unique_ptr销毁时,也会释放其持有的堆内存对象。
代码如下(示例):
#include <iostream>
#include <memory>
using namespace std;
class Test
{
private:
string s;
public:
Test(string s):s(s)
{
cout << "构造函数" << s << endl;
}
void show()
{
cout << s << endl;
}
~Test()
{
cout << "析构函数" << s << endl;
}
};
int main()
{
{
unique_ptr<Test> ap1(new Test("A"));
// 屏蔽了复制的功能
// unique_ptr<Test> ap2(ap1); 错误
// unique_ptr<Test> ap3=ap2; 错误
// 相同
cout << ap1.get() << endl;
ap1.get()->show();
ap1.reset(new Test("B"));
// 相同
// ap1.release();
}
cout << "主函数执行结束" << endl;
return 0;
}
3.shared_ptr
因为std::unique_ptr对其持有的资源具有独占性,所以需要使用share_ptr来在多个智能指针之间共享其持有的资源。
shared_ptr 是对unique_ptr和auto_ptr的改进版
有几个特点:
1.创建shared_ptr指针的时候可以使用make_shared<类型名>(类的参数)
对比new之后再将类传给指针的做法更加安全,防止了内存泄漏的危险
2.可以使用内置函数 .use_count()查看被引用次数,也就是赋值次数,这也是比auto_ptr好的一点
例如:
sp1 和 sp2, sp2引用的sp1,那么sp2.use_count()和sp1.use_count()的结果都是一样的
3.shared_ptr是没有.release函数的,但是可以通过 .reset()函数来进行释放,只需要不写参数
#include <iostream>
#include <memory>
using namespace std;
class Test
{
private:
string s;
public:
Test(string s):s(s)
{
cout << "构造函数" << s << endl;
}
void show()
{
cout << s << endl;
}
~Test()
{
cout << "析构函数" << s << endl;
}
};
int main()
{
{
shared_ptr<Test> sp1(new Test("A"));
cout << sp1.get() << endl;
sp1.get()->show();
// 新增构建方式
shared_ptr<Test> sp2 = make_shared<Test>("B");
cout << sp2.get() << endl;
sp2.get()->show();
}
cout << "主函数执行结束" << endl;
return 0;
}
shared_ptr比auto_ptr优秀地方在于其内部会对资源的多次引用计数,这样尽管支持复制,但是可以精细的管理持有的资源。
需要注意的是shared_ptr没有release函数。
#include <iostream>
#include <memory>
using namespace std;
class Test
{
private:
string s;
public:
Test(string s):s(s)
{
cout << "构造函数" << s << endl;
}
void show()
{
cout << s << endl;
}
~Test()
{
cout << "析构函数" << s << endl;
}
};
int main()
{
shared_ptr<Test> sp3;
{
cout << "{" << endl;
shared_ptr<Test> sp1 = make_shared<Test>("A");
cout << "引用次数:" << sp1.use_count() << endl; // 1
shared_ptr<Test> sp2(sp1); // 拷贝构造函数
cout << "引用次数:" << sp1.use_count() << endl; // 2
cout << "引用次数:" << sp2.use_count() << endl; // 2
sp2.reset(new Test("B"));
cout << "引用次数:" << sp1.use_count() << endl; // 1
cout << "引用次数:" << sp2.use_count() << endl; // 1
sp3 = sp1; // 赋值运算符
cout << "引用次数:" << sp1.use_count() <<
" " << sp3.use_count() << endl;
cout << "}" << endl;
}
// 只有在计数清零的时候才会销毁持有的堆内存对象
cout << "引用次数:" << sp3.use_count() << endl;
cout << "主函数执行结束" << endl;
return 0;
}
4.weak_ptr
1)weak_ptr主要是协助shared_ptr进行工作,因为是弱关系,所以在访问时不会影响引用计数
但是如果想要访问堆内存资源对象的功能时,需要用内置函数.lock()转换成shared_ptr才能
正常进行使用
2)但是weak_ptr的关联对象一旦被释放或者关联失效,weak_ptr就无法继续获得资源
所以weak_ptr增加了一个empired()函数,功能是监测管理的指针对象的时效性,一旦被释放或者关联失效,则返回true,反之则说明资源存在,可以继续使用。
#include <iostream>
#include <memory>
using namespace std;
class Test
{
private:
string s;
public:
Test(string s):s(s)
{
cout << "构造函数" << s << endl;
}
void show()
{
cout << s << endl;
}
~Test()
{
cout << "析构函数" << s << endl;
}
};
int main()
{
shared_ptr<Test> sp1 = make_shared<Test>("A");
cout << sp1.use_count() << endl; // 1
{
weak_ptr<Test> wp1(sp1);
weak_ptr<Test> wp2 = sp1;
cout << sp1.use_count() << endl; // 1
// weak_ptr → share_ptr
shared_ptr<Test> sp2 = wp1.lock();
cout << sp1.use_count() << endl; // 2
}
cout << sp1.use_count() << endl; // 1
return 0;
}
时效性检测
weak_ptr增加了一个expired()函数来检测管理的指针对象的时效性。返回true,说明引用的资源已经不存在了,反之说明资源存在,这时候再使用lock函数。
5.四个指针的字节大小
因为shared_ptr和weak_ptr相对于前两者增加了计数引用,即多出一个32 位的数据空间,所以多4字节