1. 概念
C++中堆内存的对象在new之后使用,但是没有delete时会产生内存泄漏的问题。
由于一些其他的面向对象编程语言都提供了一些自动的垃圾回收机制,因此C++在98标准中也引入了智能指针的概念,并在C++11中趋于完善。
使用智能指针可以不用调用delete就自动回收new出的对象。智能指针对象位于栈内存,可以自动回收,此时在智能指针的析构函数中就销毁被管理的堆内存对象,防止堆内存泄漏,这样不需要手动调用delete操作内存。
C++中一共有四种智能指针:auto_ptr、unique_ptr、shared_ptr、weak_ptr,后三者是C++11引入的,第一个已经被C++11弃用(使用unique_ptr代替)。
智能指针的使用需要引入头文件 #include
2. auto_ptr
C++98的智能指针,目前已经不推荐使用。
#include <iostream>
#include <memory>
using namespace std;
class Test
{
private:
string s;
public:
Test(string s):s(s)
{
cout << s << "构造函数" << endl;
}
void show()
{
cout << s << "调用成员" << endl;
}
~Test()
{
cout << s << "析构函数" << endl;
}
};
int main()
{
cout << "主函数开始" << endl;
{
cout << "{" << endl;
// ap1管理对象A
auto_ptr<Test> ap1(new Test("A"));
// B把A顶掉了,A析构
ap1.reset(new Test("B"));
cout << "------------" << endl;
auto_ptr<Test> ap2;
ap2.reset(new Test("C"));
cout << ap2.get() << endl; // getter返回被管理的堆内存对象地址→对象C
ap2.get()->show(); // C
Test* t = new Test("D");
auto_ptr<Test> ap3(t); // ap3管D,不能手动控制D了
ap3.release(); // 回归手动释放D模式
delete t;
cout << "}" << endl;
}
cout << "主函数结束" << endl;
return 0;
}
思考】为什么auto_ptr被弃用?
auto_ptr的复制语义(主要指拷贝构造函数+赋值运算符)会造成持有的堆内存对象控制权转移给新复制出来的智能指针对象。
#include <iostream>
#include <memory>
using namespace std;
class Test
{
private:
string s;
public:
Test(string s):s(s)
{
cout << s << "构造函数" << endl;
}
void show()
{
cout << s << "调用成员" << endl;
}
~Test()
{
cout << s << "析构函数" << endl;
}
};
int main()
{
cout << "主函数开始" << endl;
{
cout << "{" << endl;
auto_ptr<Test> ap1(new Test("A"));
auto_ptr<Test> ap2(new Test("B"));
auto_ptr<Test> ap3(ap1); // 拷贝构造函数
auto_ptr<Test> ap4;
ap4 = ap2;
// 观察转移现象
cout << ap1.get() << endl; // 0
cout << ap2.get() << endl; // 0
cout << ap3.get() << endl; // 0x10d1080
cout << ap4.get() << endl; // 0x10d1220
cout << "}" << endl;
}
cout << "主函数结束" << endl;
return 0;
}
3. unique_ptr
unique_ptr作为对auto_ptr的改进,对其持有的堆内存资源具有唯一控制权,即从语法层面屏蔽了复制语义。
此时如果要进行控制权的转移,则需要配合使用move函数。
#include <iostream>
#include <memory>
using namespace std;
class Test
{
private:
string s;
public:
Test(string s):s(s)
{
cout << s << "构造函数" << endl;
}
void show()
{
cout << s << "调用成员" << endl;
}
~Test()
{
cout << s << "析构函数" << endl;
}
};
int main()
{
cout << "主函数开始" << endl;
{
cout << "{" << endl;
unique_ptr<Test> up1(new Test("A"));
unique_ptr<Test> up2(new Test("B"));
unique_ptr<Test> up3(move(up1)); // 拷贝构造函数
unique_ptr<Test> up4;
up4 = move(up2);
// 观察转移现象
cout << up1.get() << endl; // 0
cout << up2.get() << endl; // 0
cout << up3.get() << endl; // 0x881080
cout << up4.get() << endl; // 0x881220
up3.swap(up4); // 交换控制权
cout << up3.get() << endl; // 0x881080
cout << up4.get() << endl; // 0x881220
cout << "}" << endl;
}
cout << "主函数结束" << endl;
return 0;
}
4. shared_ptr
unique_ptr对其持有的资源具有唯一性,shared_ptr可以在多个对象之间共享资源。
shared_ptr有两种构建方式:
● 使用make_shared函数构建
● 使用普通构造函数构建
#include <iostream>
#include <memory>
using namespace std;
class Test
{
private:
string s;
public:
Test(string s):s(s)
{
cout << s << "构造函数" << endl;
}
void show()
{
cout << s << "调用成员" << endl;
}
~Test()
{
cout << s << "析构函数" << endl;
}
};
int main()
{
cout << "主函数开始" << endl;
{
cout << "{" << endl;
// 构建方式1:sp1→A
shared_ptr<Test> sp1 = make_shared<Test>("A");
// 构建方式2:sp2→B
shared_ptr<Test> sp2(new Test("B"));
sp1.get()->show();
sp2.get()->show();
cout << "}" << endl;
}
cout << "主函数结束" << endl;
return 0;
}
相比而言,两种构建方式前者比后者:
● 性能更好
使用前者在构建时一次性分配内存;后者要先通过new分配一次堆内存,再构建智能指针的过程中与先前的new对象一起分配第二次内存。
● 更加安全
两步操作没有保证操作的原子性,极端情况下可能出现new成功了,但是智能指针的绑定和创建失败,此时仍然会造成内存泄漏的问题。
● 内存释放延迟
一次性分配内存在释放时可能会比两次分配延迟。
引用计数:每次多一个shared_ptr对资源加以引用,计数将+1;每一个管理此资源的shared_ptr对象销毁时,计数将-1,最后一个shared_ptr销毁时,发现计数变为0,此时释放被管理的资源对象。
#include <iostream>
#include <memory>
using namespace std;
class Test
{
private:
string s;
public:
Test(string s):s(s)
{
cout << s << "构造函数" << endl;
}
void show()
{
cout << s << "调用成员" << endl;
}
~Test()
{
cout << s << "析构函数" << endl;
}
};
int main()
{
cout << "主函数开始" << endl;
shared_ptr<Test> sp3;
{
cout << "{" << endl;
shared_ptr<Test> sp1 = make_shared<Test>("A");
cout << sp1.use_count() << endl; // 1
sp1.reset();
cout << sp1.use_count() << endl; // 0
sp3 = make_shared<Test>("B");
sp1 = sp3; // 赋值运算符
cout << sp3.use_count() << endl; // 2
cout << "}" << endl;
}
cout << sp3.use_count() << endl; // 1
shared_ptr<Test> sp4(sp3); // 拷贝构造函数
cout << sp4.use_count() << endl; // 2
cout << "主函数结束" << endl;
return 0;
}
5. weak_ptr
weak_ptr并不会影响资源的销毁,是一种不影响计数的智能指针,但是在合适的时机,可以通过weak_ptr获得一个shared_ptr。weak_ptr是协助shared_ptr工作的,无法单独使用。
#include <iostream>
#include <memory>
using namespace std;
class Test
{
private:
string s;
public:
Test(string s):s(s)
{
cout << s << "构造函数" << endl;
}
void show()
{
cout << s << "调用成员" << endl;
}
~Test()
{
cout << s << "析构函数" << endl;
}
};
int main()
{
cout << "主函数开始" << endl;
shared_ptr<Test> sp1 = make_shared<Test>("A");
{
cout << "{" << endl;
weak_ptr<Test> wp1;
cout << wp1.use_count() << endl; // 0
wp1 = sp1;
cout << wp1.use_count() << " " << sp1.use_count() << endl; // 1 1
cout << "}" << endl;
}
cout << sp1.use_count() << endl; // 1
weak_ptr<Test> wp2(sp1);
cout << wp2.use_count() << endl; // 1
wp2.lock(); // 只锁定没有用
shared_ptr<Test> sp2 = wp2.lock(); // 锁定后保存
weak_ptr<Test> wp3;
wp3 = wp2;
cout << wp3.use_count() << endl; // 2
cout << sp1.use_count() << " " << sp2.use_count() << " "
<< wp2.use_count() << endl; // 2 2 2
shared_ptr<Test> sp3 = make_shared<Test>("B");
wp3 = sp3;
cout << sp3.use_count() << " " << wp3.use_count() << endl; // 1 1
sp3.reset();
cout << wp3.use_count() << endl; // 0
if(wp3.expired()) // 判断资源是否过期
{
cout << "资源已经过期" << endl;
}else
{
sp3 = wp3.lock();
cout << sp3.use_count() << endl;
}
unique_ptr<Test> up(new Test("C"));
shared_ptr<Test> sp(new Test("D"));
cout << sizeof(up) << " " << sizeof(sp) << endl; // 4 8
weak_ptr<Test> wp4 = sp;
shared_ptr<Test> sp4(wp4); // 复制语义
cout << sp4.use_count() << endl; // 2
cout << "主函数结束" << endl;
return 0;
}