今天面试C++的时候问到了智能指针,之前自己没用过智能指针,只看过一会儿,大概了解了一下它的作用,但对实现这方面不大了解。于是上CSDN搜了一下,发现有一篇博客讲的挺仔细的,作为参考:
智能指针的用处是,例如我们在程序中动态的开拓了一片空间,并把空间的地址赋予给一个指针。那么假如我们是一个粗心的程序员,忘记了在作用域的结尾把这段新开拓的空间给delete掉,那么会发上什么事情呢。
那么这个指针在作用域的结尾将被回收,也就是现在我们在这片作用域新开的空间现在没有一个指针指向他,也就是所谓的内存泄露了。
那么有什么方法可以防范这个错误呢?我们可以强制每个程序员必须谨记这条规则,但是总是会有粗心的程序员。最好的方法就是内部实现一个智能的指针,当离开其作用域的时候,不仅仅对自己执行释放,也对自己指向的内存空间进行释放。而最好的方法就是用一个类来实现了,因为类在离开作用域的时候会自动调用其析构函数,我们只需要在智能指针的析构函数里面执行对指针指向内存的释放就可以了。
下面介绍一下四种智能指针:
四种智能指针为:auto_ptr, scoped_ptr, shared_ptr, weak_ptr。
auto_ptr:最初级的智能指针,仅仅的考虑了最基本的情况。大概的实现就是这样
<template typename T> class auto_ptr { T* ptr; public: explicit auto_ptr(T* p=0):ptr(p){} ~auto_ptr() {
delete ptr; } }
使用的时候,我们用下列的语法:
auto_ptr<int> p_int(new int(0));
这样在作用域结束的时候就会自动调用auto_ptr的析构函数,删除指针指向的内存,避免内存泄漏。
要注意的是因为智能指针析构的时候会用delete来对内存进行回收,所以非动态内存是不能用智能指针来绑定的,因为一个在堆,一个在栈。所以我们应该避免以下的代码:
int num = 1;
auto_ptr<int> p_num(&num);
这里的num是在栈区的,对一个栈的内存执行delete,我不知道会怎么样。不过肯定不对就是了。
我们现在考虑一种情况,如果我们给将一个auto_ptr赋值给另外一个auto_ptr,现在两个智能指针就指向了同一片的时候,内存,当执行析构函数,总会有其中一个先知行,那么另外一个智能指针将会删除过期的内存,这是不可接受的。因此我们引入了scoped_ptr。
scoped_ptr:大体的实现跟auto_ptr差不多,但是scoped_str将拷贝构造函数作为了一个私有函数,也就是说无法把一个智能指针赋值给另一个智能指针。虽然一定程度上解决了auto_ptr的困境,不过操作起来也不方便。
所以就有了一个更加高级的智能指针shared_ptr了。
shared_ptr:在auto_ptr的基础上加入了计数功能,如果用另外一个shared_ptr来初始化,那么就会将计数器加一。当智能指针析构的时候,计数器减一,只有当计数器为0的时候才执行对内存的回收。
代码有点长,要写的东西比较多,具体的实现在上面的博客里面有详细的内容。
但是shared_ptr无发解决循环引用的问题。例如在双向链表里面,前后节点造成了互相引用,当删除一个节点的时候还有另外一个节点的指针指向他,所以造成了内存泄漏。解决这个问题可以用weak_ptr。不过不是很懂,具体的内容参照上面的博客吧。