一、概念
在C++中,堆内存对象需要手动使用delete销毁,如果忘记销毁就会导致内存泄漏,所以C++在ISO98标准中引入了智能指针的概念,并在ISO11中趋于完善。
使用智能指针可以让堆内存对象具有栈内存对象的特性,原理是给需要手动回收的堆内存对象套上一个栈内存对象的模板类对象即可。
C++中有四种智能指针,分别是:auto_ptr(自动指针,C++98已废弃),unique_ptr(唯一指针,C++11),shared_ptr(共享指针,C++11),weak_ptr(虚指针,C++11)
使用智能指针需要引入对应的头文件 #include<memory>
二、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();
// delete t1;
// ap1.reset(); // 释放控制权且销毁资源对象
// 销毁A对象释放A对象的控制权,创建B对象,管理B对象
ap1.reset(new Test("B"));
ap1.get()->show();
cout << "局部代码块执行结束" << endl;
}
cout << "程序运行结束" << endl;
return 0;
}
由于成员变量存在指针类型,因此拷贝构造函数与赋值运算符的使用会出现问题。与浅拷贝不同的是,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()
{
{ // 局部代码块
auto_ptr<Test> ap1(new Test("A"));
auto_ptr<Test> ap2(ap1); // 拷贝构造函数
cout << ap1.get() << " " << ap2.get() << endl; // 0 0x10427d0
auto_ptr<Test> ap3 = ap2; // 拷贝构造函数
cout << ap1.get() << " " << ap2.get() << " " << ap3.get() << endl; // 0 0 0x10427d0
auto_ptr<Test> ap4;
ap4 = ap3; // 赋值运算符
cout << ap1.get() << " " << ap2.get() << " " << ap3.get() << " " << ap4.get() << endl; // 0 0 0 0x10427d0
}
cout << "程序运行结束" << endl;
return 0;
}
三、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> ap2(move(up1)); // 拷贝构造函数
cout << up1.get() << " " << ap2.get() << endl; // 0 0x10427d0
unique_ptr<Test> ap3 = move(ap2); // 拷贝构造函数
cout << up1.get() << " " << ap2.get() << " " << ap3.get() << endl; // 0 0 0x10427d0
unique_ptr<Test> ap4;
ap4 = move(ap3); // 赋值运算符
cout << up1.get() << " " << ap2.get() << " " << ap3.get() << " " << ap4.get() << endl;
}
cout << "程序运行结束" << endl;
return 0;
}
四、shared_ptr
unique_ptr对资源具有独占性,多个shared_ptr对象可以共享资源。
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()
{
{ // 局部代码块
// 传统方式:使用new创建资源对象,再绑定ap1与资源
shared_ptr<Test>sp1(new Test("A"));
// 新方式:创建绑定一步到位
shared_ptr<Test>sp2= make_shared<Test>("B");
}
cout << "程序运行结束" << endl;
return 0;
}
两种创建方式的区别在于后者是一步实现(创建资源对象+关系绑定),前者分为两步完成(先创建资源对象,再进行资源绑定)。
后者优点:安全性更好,性能更好
缺点:资源释放效率低
每次多一个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 = make_shared<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
}
cout << "引用计数:" << sp3.use_count() << endl;
sp3.get()->show();
cout << "程序运行结束" << endl;
return 0;
}
五、weak_ptr
weak_ptr是一个不控制资源对象的智能指针,也不会影响资源的引用计数,其主要的目的是协助shared_ptr工作。通过weak_ptr的构造函数,参数传入一个持有资源对象的shared_ptr对象或者weak_ptr对象,即可创建。weak_ptr与对象呈现弱相关性,因此不支持get等函数直接操作资源对象。建议weak_ptr对象调用lock函数之前,先检测引用计数是否大于0,或者使用expired()检查是否可以转换为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");
cout << "引用计数:" << sp1.use_count() << endl; // 1
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_ptre中得到一个持有资源的shared_ptr对象
shared_ptr<Test> sp2 = wp1.lock();
sp2.get()->show();
cout << sp2.use_count() << endl; // 2
wp3 = wp1;
cout << wp3.use_count() << endl; // 2
}
cout << wp3.use_count() << endl; // 0
if(wp3.expired())
{
cout << "无法使用lock函数" << endl;
}
else
{
cout << "可以使用lock函数" << endl;
}
cout << "程序运行结束" << endl;
return 0;
}