智能指针的概念:
C++中堆内存对象在new之后创建,但是如果忘记delete则会产生内存泄露的问题。诸如Java、C#等语言则直接提供垃圾回收机制来处理不使用的对象,因此在C++98标准中引入了智能指针的概念,并在C++11中趋于完善
使用智能指针,可以让堆内存对象无需手动调用delete就能销毁
智能指针对象是一个栈内存对象,但是它可以管理一个堆内对象,在智能指针对象销毁时,自动触发析构函数,在析构函数中销毁被管理的堆内存对象,从而防止堆内存对象的内存泄露
根据智能指针拷贝/赋值处理不同,可以分为三种实现形式:
(1)拷贝/赋值时分配新的地址空间,拷贝原指针指向的内容
(2)拷贝/赋值时将右边的空间和地址传递给左边,右边会是失去原来的地址空间
(3)拷贝/赋值是共享地址空间,同时增加一个引用计数(记录使用地址空间的对象个数),当引用计数为0的时候释放地址空间
C++11中有四种智能指针:
后三着是C++11支持的内容,第一个已经被C++11弃用,因此不推荐使用
注意:
智能指针类的使用需要引入头文件 #include <memory>
(1)std::auto_ptr
auto_ptr是C++98的智能指针,目前已经不推荐使用
示例代码:
#include <iostream>
#include <memory> // 头文件
using namespace std;
class Test
{
public:
Test(string name):name(name)
{
cout << name << "构造函数" << endl;
}
void show()
{
cout << name << "成员函数" << endl;
}
~Test()
{
cout << name << "析构函数" << endl;
}
private:
string name;
};
int main()
{
{
cout << "{" << endl;
Test *t1 = new Test("A");
// 创建智能指针对象ap1,并管理资源对象A
auto_ptr<Test> ap1(t1);
// delete t1; 错误
cout << ap1.get() << " " << t1 << endl; // 0x1232468 0x1232468
// 通过智能指针对象调用被管理对象的成员
ap1.get()->show();
// 创建智能指针对象ap2,并管理资源对象B
auto_ptr<Test> ap2(new Test("B"));
// 使用资源对象C顶替掉ap2中的B
ap2.reset(new Test("C")); // B被顶替,销毁
// ap2.release(); // 释放对资源对象的管理,且不销毁资源对象
ap2.reset(); // 释放对资源对象的管理,且销毁资源对象
cout << "}" << endl;
}
cout << "主函数结束" << endl;
return 0;
}
auto_ptr被废弃的原因:
与之前的浅拷贝类似,当auto_ptr面临复制语义时(拷贝构造函数、赋值运算符等)会出现对资源对象的控制权转移问题
(2)std::unique_ptr
作为对auto_ptr的改进,unique_ptr对其持有的堆内存资源具有唯一控制权,即unique_ptr不可以通过传统语法复制
示例代码:
#include <iostream>
#include <memory> // 头文件
using namespace std;
class Test
{
public:
Test(string name):name(name)
{
cout << name << "构造函数" << endl;
}
void show()
{
cout << name << "成员函数" << endl;
}
~Test()
{
cout << name << "析构函数" << endl;
}
private:
string name;
};
int main()
{
{
cout << "{" << endl;
unique_ptr<Test> up1(new Test("A"));
unique_ptr<Test> up2(new Test("B"));
// unique_ptr<Test> up3(up1); 错误
unique_ptr<Test> up4;
// up4 = up2; 错误
up1.get()->show();
up2.get()->show();
cout << "}" << endl;
}
cout << "主函数结束" << endl;
return 0;
}
unique_ptr可以使用move函数完成控制权的转移,提升了语法门槛,降低无意中转移的概率。
(3)std::shared_ptr
unique_ptr对资源具有独占性,而shared_ptr可以把资源对象在多个shared_ptr之间进行共享
shared_ptr有两种初始化对象的方法:
(1)资源对象使用new
(2)使用make_shared函数
示例代码:
#include <iostream>
#include <memory> // 头文件
using namespace std;
class Test
{
public:
Test(string name):name(name)
{
cout << name << "构造函数" << endl;
}
void show()
{
cout << name << "成员函数" << endl;
}
~Test()
{
cout << name << "析构函数" << endl;
}
private:
string name;
};
int main()
{
{
cout << "{" << endl;
// 使用new初始化
shared_ptr<Test> sp1(new Test("A"));
// 使用make_shared函数初始化
shared_ptr<Test> sp2 = make_shared<Test>("B");
sp1.get()->show();
sp2.get()->show();
cout << "}" << endl;
}
cout << "主函数结束" << endl;
return 0;
}
这两种初始化防止各有利弊,后者相对前者:
(1)性能更好
(2)更加安全
(3)内存释放效率低
(4)std::weak_ptr
weak_ptr是对资源的一种弱持有的关系,本身无法影响资源对象的销毁,只是为了协助shared_ptr进行工作,因此也不会影响计数。
示例代码:
#include <iostream>
#include <memory> // 头文件
using namespace std;
class Test
{
public:
Test(string name):name(name)
{
cout << name << "构造函数" << endl;
}
void show()
{
cout << name << "成员函数" << endl;
}
~Test()
{
cout << name << "析构函数" << endl;
}
private:
string name;
};
int main()
{
shared_ptr<Test> sp1 = make_shared<Test>("A");
{
cout << "{" << endl;
weak_ptr<Test> wp1;
cout << wp1.use_count() << endl; // 0
wp1 = sp1; // 赋值运算符(其它的复制语义也可以)
cout << sp1.use_count() << " " << wp1.use_count() << endl; // 1 1
// wp1.get(); 错误
// sp1.reset(); // 释放控制权,并减少引用计数1,如果1→0,则销毁
if(wp1.expired()) // 失效性检测
{
cout << "wp1抱的大腿没了!!" << endl;
}else
{
// 从“备胎”里,获取一个sp对象
shared_ptr<Test> sp2 = wp1.lock();
cout << sp1.use_count() << " "
<< wp1.use_count() << " " << sp2.use_count() << endl; // 2 2 2
sp2.get()->show();
// lock函数可以反复使用
shared_ptr<Test> sp3 = wp1.lock();
cout << sp3.use_count() << endl; // 3
wp1.lock(); // 未保存返回值
cout << sp3.use_count() << endl; // 3
}
cout << sp1.use_count() << endl; // 1
cout << "}" << endl;
}
cout << sp1.use_count() << endl; // 1
cout << "主函数结束" << endl;
return 0;
}