尽管如此,我仍然觉得还是应该自己总结一下知识点,梳理自己的知识体系
手动管理内存会造成内存泄露,现在C++的智能指针可以在很大程度上帮我们缓解这个问题,降低我们手动管理内存的心智负担。
智能指针类型
- shared_ptr
- unique_ptr
- weak_ptr
共享指针shared_ptr
共享指针会记录有多少个(共享)指针指向同一个物体,当这个数字降为0的时候,程序会自动释放资源,省去我们手动delete的烦恼。
要使用共享指针shared_ptr首先要引入#include<memory>
,其次,shared_ptr等名字都是在std命名空间中,所以使用的时候前面要加std::
为了简洁,后面我们假设using namespace std;
初始化共享指针
方式一:make_shared
定义并初始化一个shared_ptr,make_shared会动态分配一块内存,创建对应的资源,然后让shared_ptr指向他,他是一个模板,可以接受一个类型+对应的初始化参数
#include<memory>
using namespace std;
shared_ptr<int> p;
p = make_shared<int>(100);
方式二:new
除了make_shared,你也可以用new初始化shared_ptr
#include<memory>
using namespace std;
shared_ptr<int> p {new int(100)};
定义之后,你可以像普通的指针一样使用*、→之类的运算符操作数据,并且多个共享指针可以指向同一个物体,共享控制权。比如
shared_ptr<int> p {new int(100)};
shared_ptr<int> p2 = p;
cout << *p << endl;
*p2 = 321;
cout << *p << endl;
推荐使用make_shared的初始化方式,因为效率更高更安全
自动管理内存
定义一个Ball的类,如下:
class Ball
{
public:
Ball() { cout << "A ball appears." << endl; }
~Ball() { cout << "A ball disappears." << endl; }
void Bounce() { cout << "A ball jumps." << endl; }
};
定义完毕,当我们创建一个Ball实例的时候,会打印A ball appears. 当我们销毁一个Ball实例的时候,会打印A ball disappears. Ball里面定义了一个公有成员函数Bounce,调用的时候会打印A ball jumps.
下面我们用共享指针来管理Ball的创建与销毁,同时展示一些shared_ptr的基本用法
...
int main()
{
shared_ptr<Ball> p = make_shared<Ball>;
cout << p.use_count() << endl;//1
shared_ptr<Ball> p2 = p;
cout << p.use_count() << " " << p2.use_count() << endl;//2 2
shared_ptr<Ball> p3 = p;
cout << p.use_count() << " " << p2.use_count() << p3.use_count() << endl;//3 3 3
p.reset();
p2.reset();
p3.reset();
}
运行结果:
A ball appears.
1
2 2
3 3 3
A ball disappears.
-
创建:不同于裸指针(例如int *),共享指针会记录有多少个共享指针指向同一个物体。
上面的例子中,创建了3个共享指针指向同一个Ball
,然后依次调用use_count()
获知有多少shared_ptr
指向这个物体
可以看到创建第一个时打印1,将p2赋值为p时打印两个2,将p3赋值为p时打印三个3。 -
销毁:调用3个reset,shared_ptr调用reset之后会重置,不再指向(拥有)原来的物体。三个shared_ptr都重置之后,没有shared_ptr指向开头的Ball,它就自动释放了,所以打印
A ball disappears
对比裸指针和共享指针:
裸指针:如果其指向的资源是动态分配的内存,且未被手动释放,那么即便指向这块资源的所有裸指针都被摧毁了,这块资源仍然可能未被释放。
共享指针:共享指针可以在很大程度上帮我们解决这个问题,因为当指向某个物体的共享指针个数降为0的时候,这个物体就会自动销毁。这就是智能指针如何帮助我们管理内存,以避免内存泄漏。
引用计数原理
顾名思义,引用计数的意思就是,数一数有多少个共享指针指向某个物体。
假设有一个物体O,我们创建一个共享指针指向它,这时引用计数变为1,再复制一个这样的共享指针,引用计数变为2。
当某一个共享指针reset或者销毁的时候,引用计数减少1.
如果某个物体的引用计数降为0,程序就会自动释放这个物体。
这样就不需要我们手动delete某个资源,大大降低在复杂环境下人类出错的概率。
获取智能指针内部的裸指针
如果因为特殊的原因,你需要获得指向物体的裸指针,比如某个函数接受的参数是裸指针。那么你可以用get()
方法可以获取智能指针内部的裸指针。
Ball* rp = p.get();
注意
即便你仍然有裸指针指向这块内存,当所有的共享指针都被摧毁的时候,底下的资源仍然会被释放,这是需要注意的地方。
因此,良好的习惯是,用共享指针的时候避免跟裸指针混用,不当心可能会访问被释放的资源,这是未定义的行为,可能会造成程序崩溃,还很难修复。
【下一篇进阶用法没看懂,有空再补。。。】