目录
1、 概念
堆内存空间的对象需要手动使用delete销毁,如果忘记使用delete销毁就会造成内存泄漏。
所以C++在ISO 98标准中引入了智能指针的概念,并在C++11中趋于完善。
使用智能指针可以让堆内存对象具有栈内存对象的特点,原理是给需要手动回收的堆内存对象套上一层栈内存的模板类对象即可。
C++有四种智能指针:
● auto_ptr(自动指针)(C++ ISO 98)(已废弃)
● unique_ptr(唯一指针)(C++ ISO 11)
● shared_ptr(共享指针)(C++ ISO 11)
● weak_ptr(虚指针)(C++ ISO 11)
使用智能指针都需要引入对应的头文件#include<memory>
2、 auto_ptr(熟悉)
#include <iostream>
#include <memory>
using namespace std;
class Test
{
private:
string s;
public:
Test(string s):s(s)
{
cout << s << "构造函数" << endl;
}
~Test()
{
cout << s << "析构函数" << endl;
}
void show()
{
cout << s << "程序执行" << endl;
}
};
int main()
{
{
Test *t1 = new Test("A");
// 创建一个栈内存智能指针对象ap1
auto_ptr<Test> ap1(t1); // ap1管理t1
// 取出被管理的堆内存对象,并调用show成员函数
ap1.get()->show();
// 释放ap1智能指针对t1对象的控制权
// ap1.release();
// 释放控制权且销毁资源对象
// ap1.reset();
// 创建B对象,销毁A对象,管理B对象
ap1.reset(new Test("B"));
ap1.get()->show();
cout << "局部代码块执行结束" << endl;
}
cout << "程序执行结束" << endl;
return 0;
}
#include <iostream>
#include <memory>
using namespace std;
class Test
{
private:
string s;
public:
Test(string s):s(s)
{
cout << s << "构造函数" << endl;
}
~Test()
{
cout << s << "析构函数" << endl;
}
void show()
{
cout << s << "程序执行" << endl;
}
};
int main()
{
{
auto_ptr<Test> ap1(new Test("A"));
auto_ptr<Test> ap2(ap1); // 调用拷贝构造函数
cout << ap1.get() << " " << ap2.get() << endl; // 0 0xfd27e0
auto_ptr<Test> ap3 = ap2; // 调用拷贝构造函数
cout << ap1.get() << " " << ap2.get() << " " << ap3.get() << endl; // 0 0 0x7f27e0
auto_ptr<Test>ap4;
ap4 = ap3; // 运算符重载
cout << ap1.get() << " " << ap2.get() << " " << ap3.get() << " " << ap4.get() << endl; // 0 0 0 0x10e27e0
}
cout << "程序执行结束" << endl;
return 0;
}
3、unique_ptr(熟悉)
作为对auto_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;
}
~Test()
{
cout << s << "析构函数" << endl;
}
void show()
{
cout << s << "程序执行" << endl;
}
};
int main()
{
{
unique_ptr<Test> up1(new Test("A"));
unique_ptr<Test> up2(move(up1)); // 调用拷贝构造函数
cout << up1.get() << " " << up2.get() << endl; // 0 0xfd27e0
unique_ptr<Test> up3 = move(up2); // 调用拷贝构造函数
cout << up1.get() << " " << up2.get() << " " << up3.get() << endl; // 0 0 0x7f27e0
unique_ptr<Test>up4;
up4 = move(up3); // 运算符重载
cout << up1.get() << " " << up2.get() << " " << up3.get() << " " << up4.get() << endl; // 0 0 0 0x10e27e0
}
cout << "程序执行结束" << endl;
return 0;
}
4、shared_ptr(掌握)
unique_ptr对资源具有独占性,多个shared_ptr对象可以共享资源。
shared_ptr有两种创建方式。
两种创建方式区别在于后者是一步实现(创建资源对象+关系绑定),前者分为两步完成(先创建资源对象,再绑定关系)。
后者优点:
● 安全性更好
● 性能更好
后者缺点:
● 资源释放效率低
每多一个shared_ptr对资源进行管理,引用计数将+1,每个指向改对象的shared_ptr对象销毁时,引用计数-1。最后一个shared_ptr对象销毁时,计数清零,资源对象销毁。
#include <iostream>
#include <memory>
using namespace std;
class Test
{
private:
string s;
public:
Test(string s):s(s)
{
cout << s << "构造函数" << endl;
}
~Test()
{
cout << s << "析构函数" << endl;
}
void show()
{
cout << s << "程序执行" << endl;
}
};
int main()
{
shared_ptr<Test> sp3;
{
shared_ptr<Test> sp1(new Test("A"));
cout << sp1.use_count() << endl; // 1
shared_ptr<Test> sp2(sp1); // 拷贝构造函数
cout << "引用计数:" << sp2.use_count() << endl; // 2
sp3 = sp2;
cout << "引用计数:" << sp3.use_count() << endl; // 3
shared_ptr<Test> sp4(new Test("B"));
cout << sp4.use_count() << endl; // 1
}
cout << "引用计数:" << sp3.use_count() << endl; // 1
sp3.get()->show();
cout << "程序执行结束" << endl;
return 0;
}
5、weak_ptr(熟悉)
weak_ptr是一个不控制资源对象的智能指针,也不会影响资源的引用计数,其主要的目的是协助shared_ptr工作。
通过weak_ptr的构造函数,参数传入一个持有资源对象的shared_ptr对象或weak_ptr对象,即可创建。
weak_ptr与资源对象呈现弱相关性,因此不支持get等函数直接操作资源对象。
建议weak_ptr调用lock函数之前,先检测引用计数是否大于0,或使用expried()函数检测是否可转换为shared_ptr.
#include <iostream>
#include <memory>
using namespace std;
class Test
{
private:
string s;
public:
Test(string s):s(s)
{
cout << s << "构造函数" << endl;
}
~Test()
{
cout << s << "析构函数" << endl;
}
void show()
{
cout << s << "程序执行" << endl;
}
};
int main()
{
weak_ptr<Test>wp3;
{
shared_ptr<Test>sp1 = make_shared<Test>("A");
weak_ptr<Test> wp1 = sp1;
cout << sp1.use_count() << endl; // 1
cout << wp1.use_count() << endl; // 1
weak_ptr<Test> wp2(wp1); // 拷贝构造函数
cout << wp2.use_count() << endl; // 1
// 从weak_ptr中得到一个持有资源的shared_ptr对象
shared_ptr<Test> sp2 = wp1.lock();
cout << wp2.use_count() << endl; // 2
wp3 = wp1;
cout << wp3.use_count() << endl; // 2
}
cout << wp3.use_count() << endl; // 0
if(wp3.expired())
{
cout << "无法使用lock函数" << endl;
}
cout << "程序执行结束" << endl;
return 0;
}
6、思考题(了解)
手写一个共享指针类SharedPtr,要求实现:
● 构造函数
● 拷贝构造函数
● 赋值运算符重载
● get函数
● use_count函数
● reset函数
● 析构函数
#include <iostream>
#include <memory>
using namespace std;
template<class T>
class SharedPtr
{
private:
T *res = NULL; // 资源指针
int *count = NULL; // 引用计数
public:
SharedPtr(T *t):res(t),count(new int(1)){}
// 拷贝构造函数
SharedPtr(const SharedPtr &sp):res(sp.res),count(sp.count)
{
(*count)++; // 计数+1
}
// 赋值运算符重载
SharedPtr & operator =(const SharedPtr &sp)
{
if(&sp != this)
{
// 销毁原来的资源
reset();
res = sp.res;
count = sp.count;
(*count)++;
}
return *this;
}
void reset()
{
(*count)--; // 计数-1
if(*count == 0)
{
delete res;
delete count;
}
res = NULL;
count = NULL;
}
T* get()const
{
return res;
}
int use_count()const
{
return *count;
}
~SharedPtr()
{
reset();
}
};
class Test
{
private:
string s;
public:
Test(string s):s(s)
{
cout << s << "构造函数" << endl;
}
~Test()
{
cout << s << "析构函数" << endl;
}
void show()
{
cout << s << "程序执行" << endl;
}
};
int main()
{
SharedPtr<Test> sp1(new Test("A"));
sp1.get()->show();
cout << sp1.use_count() << endl; // 1
SharedPtr<Test> sp2(sp1);
cout << sp2.use_count() << endl; // 2
SharedPtr<Test> sp3(new Test("B"));
cout << sp3.use_count() << endl; // 1
sp3 = sp2;
cout << sp3.use_count() << endl;
return 0;
}