一、智能指针的概念
C++中堆内存对象需要手动使用new来创建,如果忘记delete操作则会造成内存泄漏的问题。诸如Java、C#等语言则直接提供垃圾回收机制来处理不使用的对象,因此C++在ISO 98标准中也引入了智能指针的概念,并在C++11中趋于完善。
使用智能指针,可以让堆内存的对象不使用delete就可以在特定的时机销毁。
智能指针对象位于栈内存,管理一个堆内存的对象,当智能指针对象的生命周期结束后,会在析构函数中释放掉管理的堆内存资源,从而防止堆内存对象内存泄漏。
C++中有四种智能指针:auto_ptr(自动指针)、unique_ptr(唯一指针)、shared_ptr(共享指针)与weak_ptr(虚指针),其中后三者是C++11中引入的,auto_ptr在C++11以后已经被弃用。
使用智能指针需要引入头文件 #include
二、auto_ptr
基础使用如下。
#include <iostream>
#include <memory>
using namespace std;
class Test
{
private:
string name;
public:
Test(string name):name(name)
{
cout << name << "构造函数" << endl;
}
void show()
{
cout << name << "成员函数" << endl;
}
~Test()
{
cout << name << "析构函数" << endl;
}
};
int main()
{
{
Test* t = new Test("A");
auto_ptr<Test> ap1(t); // 智能指针
// 从智能指针对象中取出被管理的资源对象
Test* t2 = ap1.get();
t2->show();
// delete t2; 错误:既然已经放手了,就别再插手
// 解除ap1对资源的管理,ap1仅仅放弃管理权,不会销毁资源
ap1.release();
delete t;
//ap2持有资源B
auto_ptr<Test> ap2(new Test("B"));
// 解除ap2对资源的管理并销毁资源对象
ap2.reset();
auto_ptr<Test> ap3(new Test("C"));
// ap3持有的资源从C更换为D,C销毁
ap3.reset(new Test("D"));
ap3.get()->show();
}
cout << "主函数执行结束" << endl;
return 0;
}
auto_ptr容易让人产生误用的地方是其复制语义,当执行复制相关操作时(拷贝构造函数、赋值运算符),会出现资源控制权转移的问题
#include <iostream>
#include <memory>
using namespace std;
class Test
{
private:
string name;
public:
Test(string name):name(name)
{
cout << name << "构造函数" << endl;
}
void show()
{
cout << name << "成员函数" << endl;
}
~Test()
{
cout << name << "析构函数" << endl;
}
};
int main()
{
{
auto_ptr<Test> ap1(new Test("A"));
// 显式调用拷贝构造函数
auto_ptr<Test> ap2(ap1);
// 隐式调用拷贝构造函数
auto_ptr<Test> ap3 = ap2;
auto_ptr<Test> ap4;
ap4 = ap3; // 赋值运算符
// 指针转移
cout << ap1.get() << endl; // 0
cout << ap2.get() << endl; // 0
cout << ap3.get() << endl; // 0
cout << ap4.get() << endl; // 0x7a0fa8
// 忘记转移了。。。
// ap1.get()->show(); 错误
}
cout << "主函数执行结束" << endl;
return 0;
}
三、unique_ptr
unique_ptr作为对auto_ptr的改进,其对象对资源具有唯一的控制权,无法通过常规的复制语义进行控制权的转移。
可以通过move函数来完成控制权的转移。
#include <iostream>
#include <memory>
using namespace std;
class Test
{
private:
string name;
public:
Test(string name):name(name)
{
cout << name << "构造函数" << endl;
}
void show()
{
cout << name << "成员函数" << endl;
}
~Test()
{
cout << name << "析构函数" << endl;
}
};
int main()
{
{
unique_ptr<Test> up1(new Test("A"));
// 显式调用拷贝构造函数
// unique_ptr<Test> up2(up1); 错误
// 隐式调用拷贝构造函数
// unique_ptr<Test> up3 = up2; 错误
unique_ptr<Test> up4;
// 赋值运算符
// up4 = up3; // 错误
// 之前的复制语义结合move函数仍然可以转移控制权
unique_ptr<Test> up2(move(up1));
unique_ptr<Test> up3 = move(up2);
up4 = move(up3);
cout << up1.get() << endl; // 0
cout << up2.get() << endl; // 0
cout << up3.get() << endl; // 0
cout << up4.get() << endl; // 0xc50fa8
unique_ptr<Test> up5(new Test("B"));
up4.swap(up5); // 交换控制权
up4.get()->show(); // B成员函数
up5.get()->show(); // A成员函数
}
cout << "主函数执行结束" << endl;
return 0;
}
四、shared_ptr
unique_ptr对持有的资源具有独占性,而shared_ptr则可以把持有的资源在多个shared_ptr对象之间共享。
shared_ptr有两种初始化的方式:
" << endl;
}
void show()
{
cout << name << "成员函数" << endl;
}
~Test()
{
cout << name << "析构函数" << endl;
}
};
int main()
{
{
// 使用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;
return 0;
}
make_shared函数相比传统new的方式区别如下:
1. 性能略高
2. 安全性更好
3. 内存释放可能延迟
推荐使用make_shared来创建shared_ptr对象。
每多一个shared_ptr持有同一资源,每个share_ptr内部的引用计数将增加1,每一个持有该资源的shared_ptr销毁时,引用计数将减1,直到引用计数减为0,则销毁持有的资源。
#include <iostream>
#include <memory>
using namespace std;
class Test
{
private:
string name;
public:
Test(string name):name(name)
{
cout << name << "构造函数" << endl;
}
void show()
{
cout << name << "成员函数" << endl;
}
~Test()
{
cout << name << "析构函数" << endl;
}
};
int main()
{
shared_ptr<Test> sp4;
{
cout << "{" << endl;
shared_ptr<Test> sp1= make_shared<Test>("A");
cout << "引用计数:" << sp1.use_count() << endl; // 1
shared_ptr<Test> sp2(sp1);
shared_ptr<Test> sp3 = sp2;
sp4 = sp3;
cout << "引用计数:" << sp1.use_count() << endl; // 4
cout << "引用计数:" << sp2.use_count() << endl; // 4
cout << "引用计数:" << sp3.use_count() << endl; // 4
cout << "引用计数:" << sp4.use_count() << endl; // 4
cout << "}" << endl;
}
cout << "引用计数:" << sp4.use_count() << endl; // 1
cout << "主函数执行结束" << endl;
return 0;
}
再补充几个操作函数:
#include <iostream>
#include <memory>
using namespace std;
class Test
{
private:
string name;
public:
Test(string name):name(name)
{
cout << name << "构造函数" << endl;
}
void show()
{
cout << name << "成员函数" << endl;
}
~Test()
{
cout << name << "析构函数" << endl;
}
};
int main()
{
shared_ptr<Test> sp4;
cout << sp4.operator bool() << endl; // false
cout << sp4.get() << endl; // 0
{
cout << "{" << endl;
shared_ptr<Test> sp1= make_shared<Test>("A");
// 判断是否是独自持有资源
cout << sp1.unique() << endl; // 1
shared_ptr<Test> sp2(sp1);
cout << sp1.unique() << endl; // 0
sp4 = sp2;
// 获得资源对象的引用
Test &t = sp2.operator *();
t.show();
// 如果持有资源,则返回true
cout << sp2.operator bool() << endl;
cout << "}" << endl;
}
cout << "主函数执行结束" << endl;
return 0;
}
五、weak_ptr
weak_ptr无法控制资源,只能协助shared_ptr进行工作。因为不控制资源,因此weak_ptr不会影响引用计数。
#include <iostream>
#include <memory>
using namespace std;
class Test
{
private:
string name;
public:
Test(string name):name(name)
{
cout << name << "构造函数" << endl;
}
void show()
{
cout << name << "成员函数" << endl;
}
~Test()
{
cout << name << "析构函数" << endl;
}
};
int main()
{
shared_ptr<Test> sp1;
{
cout << "{" << endl;
shared_ptr<Test> sp2 = make_shared<Test>("A");
sp1 = sp2;
// 创建一个weak_ptr对象
weak_ptr<Test> wp1(sp2);
weak_ptr<Test> wp2 = wp1;
weak_ptr<Test> wp3;
wp3 = sp1;
// wp3.get(); 错误:无法直接获得资源
cout << wp1.use_count() << endl; // 2
cout << wp2.use_count() << endl; // 2
cout << wp3.use_count() << endl; // 2
cout << sp1.use_count() << endl; // 2
cout << sp2.use_count() << endl; // 2
cout << "}" << endl;
}
cout << sp1.use_count() << endl; // 1
cout << "主函数执行结束" << endl;
return 0;
}
可以从weak_ptr中获得shared_ptr对象,从而持有资源。
#include <iostream>
#include <memory>
using namespace std;
class Test
{
private:
string name;
public:
Test(string name):name(name)
{
cout << name << "构造函数" << endl;
}
void show()
{
cout << name << "成员函数" << endl;
}
~Test()
{
cout << name << "析构函数" << endl;
}
};
int main()
{
shared_ptr<Test> sp1 = make_shared<Test>("A");
weak_ptr<Test> wp1(sp1);
{
cout << "{" << endl;
cout << wp1.use_count() << endl; // 1
// 通过wp1来获得shared_ptr对象
shared_ptr<Test> sp2 = wp1.lock();
sp2.get()->show();
cout << wp1.use_count() << endl; // 2
cout << "}" << endl;
}
cout << sp1.use_count() << endl; // 1
cout << "主函数执行结束" << endl;
return 0;
}
需要注意的是,weak_ptr对资源的持有可能失效,此时lock也会失效。因此,在执行lock函数之前,要先检测资源是否已经失效。
#include <iostream>
#include <memory>
using namespace std;
class Test
{
private:
string name;
public:
Test(string name):name(name)
{
cout << name << "构造函数" << endl;
}
void show()
{
cout << name << "成员函数" << endl;
}
~Test()
{
cout << name << "析构函数" << endl;
}
};
int main()
{
weak_ptr<Test> wp1;
{
cout << "{" << endl;
shared_ptr<Test> sp1 = make_shared<Test>("A");
wp1=sp1;
cout << "}" << endl;
}
// wp1持有的资源已经失效
if(wp1.expired()) // 如果已经失效则返回true
{
cout << "资源已失效!" << endl;
}else
{
shared_ptr<Test> sp2 = wp1.lock();
sp2.get()->show();
}
cout << "主函数执行结束" << endl;
return 0;
}