什么是智能指针
学过C语言的人肯定对“指针”这个概念不陌生,简单来说,指针就是“地址”。
那么智能指针是什么呢?“智能地址”吗?
准确地来说,智能指针并不能算作是指针。在《C++ Primer Plus》一书中,就这样定义到:
智能指针是行为类似于指针的类对象,但这种对象还有其他功能
可以看到,智能指针其实是一个类对象。
为啥需要智能指针
那既然已经有了指针,为什么需要智能指针呢?智能指针智能在哪里呢?
我们首先来看下面这个问题:
void remodel(std::string& str)
{
std::string * ps = new std::string(str);
...
str = *ps;
return;
}
我们会发现,每当调用的时候,该函数都会分配堆中的内存,但是从来不收回,从而导致内存泄漏。
对此问题,我们最先想到的解决方法就是在retrun
前面添加下面的语句,来释放分配的内存:
delete ps;
然而,但凡涉及到“别忘了”的解决方法,很少是最佳的。
因为我们很可能会忘记掉;
有时候可能记住了,但也有可能在不经意间删除或者注释掉了这些代码;
即使确实没有忘记,也可能有问题,请看如下变体:
void remodel(std::string& str)
{
std::string* ps = new std::string(str);
...
if(weird_thing())
throw exception();
str = *ps;
delete ps;
return;
}
当出现异常exception
时,delete
将不被执行,因此也将导致内存泄漏。
那么如何解决这个问题呢?
答案就是智能指针
了!!!
智能指针的原理
智能指针是一个类对象
,这个类的构造函数中传入了一个指针,析构函数中释放传入的指针。
由于该类对象是在栈上开辟和释放的,所以,当我们的函数(或程序)结束时就会被自动释放。
简单来说,智能指针就是利用了
栈
在函数(程序)开始时系统自动分配,结束时系统自动释放的特性,将在堆上指向开辟内存的函数指针封装在类对象的构造函数中,将指向释放内存的函数指针封装在类对象的析构函数中。
这样,内存的开辟和释放均由系统来操作,不需要人工操作,达到了智能
的目的。
智能指针的类别
智能指针有多种,尤其是C11标准
之后,又对智能指针作了进一步的补充。
我们在这里,先来看看最简单的三种智能指针:
auto_ptr
unique_ptr
shared_ptr
注意:
其中的auto_ptr
是C++98
所提供的解决方案;C++11
已经将其摒弃,并提供了另外两种解决方案。
简单案例
首先,我们需要知道,要创建智能指针对象,必须包含头文件#include <memory>
,该文件模板定义。然后使用通常的模板语法来实例化所需类型的指针。
如下程序演示了如何使用三种智能指针:
#include <iostream>
#include <string>
#include <memory>
class Report
{
private:
std::string str;
public:
Report(const std::string s) :str(s)
{
std::cout << "Object created!\n";
}
~Report() { std::cout << "Object deleted!\n"; }
void comment()const
{
std::cout << str << "\n";
}
};
int main()
{
{
std::auto_ptr<Report> ps(new Report("using auto_ptr"));
ps->comment();
}
{
std::shared_ptr<Report> ps(new Report("using shared_ptr"));
ps->comment();
}
{
std::unique_ptr<Report> ps(new Report("using unique_ptr"));
ps->comment();
}
return 0;
}
每个智能指针都放在一个代码块内,这样离开代码块时,指针将过期。
运行结果
如下是运行结果:
可以看到,程序运行时,对象自己就会析构掉,灰常的智能。
参考资料
【1】Stephen Prata. C++ Primer Plus(第六版)中文版. 北京:人民邮电出版社. 2020.1:35.