在动态内存管理中是使用delete和new 实现,单是动态内存管理经常会出现两种情况,一种是忘记释放内存,会造成内存泄漏;一种是尚有指针引用内存的情况下释放它,就回生成非法的指针(悬空指针)
智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象。标准库提供的两种智能指针的区别在于管理底层指针的方法不同,shared_ptr允许多个指针指向同一个对象,unique_ptr则“独占”所指向的对象。标准库还定义了一种名为weak_ptr的伴随类,它是一种弱引用,指向shared_ptr所管理的对象,这三种智能指针都定义在memory头文件中。
智能指针本质就是一个类模板。它可以创建任意的类型的指针对象,当智能指针对象使用完后,对象就会自动调用析构函数去释放该指针指向的空间
template<class T >
class smartptr//自定义指针
{
public:
smartptr(T*_ptr);//构造函数
:ptr(_ptr)
{}
~smartptr()//析构函数
{
if(ptr != nullptr)
{
cout << "smartptr:delete" << endl;
delete ptr;
ptr = nullptr;
}
}
//智能指针可以使用* 和->去获取指针对象,我们对这两个符号进行重载
T & operator*()
{
return *ptr
}
T *operator->()
{
return ptr
}
private:
T* ptr;//指针对象
}
shared_ptr
1、shared_ptr采用的是引用计数原理来实现多个shared_ptr对象之间共享资源
2、指针内部维护这一个引计数,用来记录这份资源被多少个对象使用
该指针由两部分组成
1、一个是指向堆上创建的对象的裸指针raw_ptr
2、一个指向内部隐藏的、共享的管理对象:share_count_object,其中use_count函数用于获取堆上对象被多少对象引用了,就是计数
shared_ptr<string> p1;
shared_ptr<list<int>>p2;
默认初始化的智能指针中保存着一个空指针。
智能指针的使用方式和普通指针类似,解引用一个智能指针返回它指向的对象,在一个条件判断中使用智能指针就是检测它是不是空。
if(p1 && p1->empty())
*p1 = "hi";
如下表所示是shared_ptr和unique_ptr都支持的操作:
如下表所示是shared_ptr特有的操作:
make_shared函数:
最安全的分配和使用动态内存的方法就是调用一个名为make_shared的标准库函数,此函数在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr。头文件和share_ptr相同,在memory中
必须指定想要创建对象的类型,定义格式见下面例子
//生成一个指向值为42的内存空间的shared_ptr指针
shared_ptr<int> p3 = make_shared<int>(42);
//生成一个指向值10个9的内存空间的shared_ptr指针
shared_ptr<string> p4 = make_shared<string>(10,'9');
//生成一个指向值为0的内存空间的shared_ptr指针
shared_ptr<int> p5 = make_shared<int>();
shared_ptr和new结合使用
shared_ptr<double> p1;
shared_ptr<int> p2(new int(42));
我们不能将一个内置指针隐式转换为一个智能指针
shared_ptr<int> p1 = new int (1024);//错误:必须使用直接初始化形式
shared_ptr<int> p2(new int(1024));//正确:使用了直接初始化
shared_ptr<int> clone(int p){
return new int(p); //错误:隐式转换
}
shared_ptr<int> clone(int p){
return shared_ptr<int>(new int(p)); /正确:显式
}
初始化
// 初始化方式1
std::shared_ptr<int> p1(new int(1));
// 初始化方式2
std::shared_ptr<int> p2 = p1;
// 初始化方式3
std::shared_ptr<int> p3;
p3.reset(new int(1));
if(p3) {
cout << "p3 is not null";
}
// 初始化方式4
auto sp1 = make_shared<int>(100); //相当于 shared_ptr<int> sp1(new int(100));
推荐使用make_shared创建智能指针,高效
use_count ()获得当前观察资源的引用计数
#include <iostream>
#include <memory>
using namespace std;
void test(shared_ptr<int> sp) {
// 引用计数此时应该是2
cout << "test sp.use_count()" <<sp.use_count() << endl;
}
int main()
{
std::shared_ptr<int> p1;
p1.reset(new int(1)); // 分配资源
std::shared_ptr<int> p2 = p1;
// 引用计数此时应该是2
cout << "p2.use_count() = " << p2.use_count()<< endl; // 2
// 引用计数此时应该是1
cout << "p2.use_count()= " << p2.use_count() << endl; // 1
p1.reset(); // 释放资源
// 智能指针可以通过重载的 bool 类型操作符来判断
if(!p1) {
cout << "p1 is empty\n";
}
if(!p2) {
cout << "p2 is empty\n";
}
p2.reset(); // 释放资源
// 引用计数此时应该是0
cout << "p2.use_count()= " << p2.use_count() << endl; // 0
if(!p2) {
cout << "p2 is empty\n";
}
shared_ptr<int> sp5(new int(100));
test(sp5);
// 引用计数此时应该是1
cout << "sp5.use_count()" << sp5.use_count() << endl;
return 0;
}
weak_ptr
shared_ptr虽然好用但是也会有缺点(循环引用)
假设我们要使用定义一个双向链表,如果我们想要让创建出来的链表的节点都定义成shared_ptr智能指针,那么也需要将节点内的_ptr和next都定义成shared_ptr类的智能指针不然无法进行赋值给智能指针
这个是shared_ptr的一个延伸出来的
share_ptr 虽然已经很好用了,但是有一点 share_ptr 智能指针还是有内存泄露的情况:例如当两个 shared_ptr 相互引用时,析构时两个资源时引⽤计数会减⼀,但是两者引⽤计数还是为1,永远不可能下降为0,也就是资源永远不会释放,从而导致内存泄漏。
weak_ptr对象指向shared_ptr对象时,不会增加shared_ptr中的引用计数,因此当node1销毁掉时,则node1指向的空间就会被销毁掉,node2类似,所以weak_ptr指针可以很好解决循环引用的问题
weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象(弱引用)。进行该对象的内存管理的是那个强引用的 shared_ptr, weak_ptr 只是提供了对管理对象的一个访问手段(也就是说它只引用不计数)。weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。
auto p = make_shared<int>(42);
weak_ptr<int> wp(p); //wp弱共享p,p的引用计数器未改变
if(auto np = wp.lock()){}
unique_ptr
它直接将拷贝构造函数和赋值重载函数禁用,所有不能进行拷贝和赋值
unique_ptr<string> p1(new string("abc"));//new 会返回一个空间的指针,然后直接构造初始化生成智能指针
unique_ptr<string> p2(p1.release());//将所有权从p1转移给p2,release将p1置为空
unique_ptr<string> p3(new string("Text"));