文章目录
本文讲解三个智能指针模板类,他们的对象 类似于指针,即可以把new获得的地址赋给他们的对象;而智能指针过期时,他们的 析构函数就会自动释放内存,而无需程序员自己记住释放内存。
auto_ptr
,C++98提供,C++11已经摈弃,但是已经有很多代码用了它,所以还是要看得懂。并且,后面两个新增的类的行为,其实和它相同。unique_ptr
,C++11提供shared_ptr
,C++11提供
其实还有一个智能指针模板类(即一共有4种),
weak_ptr
,但是C++ primer这本书不讨论它,不知道为啥。
智能指针:类似指针的类对象
智能指针的行为类似于指针,但是本质是类的对象,所以他还有其他功能,不仅仅是充当指针的角色。
从对象指针和常规指针的对比引入智能指针
内存管理上最可怕的就是内存泄漏,智能指针就可以缓解这一问题。
前面咱们在讲异常的栈解退部分时,曾经说过异常可能使得内存泄漏,当时对比了string类对象用new分配内存和int用new分配内存,发现string类对象分配的内存一定不会受到异常的影响,一定可以调用到析构函数以释放new分配的内存,从而绝对不会内存泄漏;但是int等类型的指针却没有类的后台庇护,一旦遇到异常,函数异常终止,无法执行发生异常的代码后面的delete代码,于是仍然会造成内存泄漏。
把示例代码拿出来再看看:
string类的对象,就算异常导致程序提前终止,也不会内存泄漏
void remodel(std::string & str)
{
std::string * ps = new std::string(str);//对象指针
···
if(weird_thing)
throw exception();
str = *ps;
delete ps;//一旦发生异常,这句代码根本不被执行,但ps变量从栈内存中删除时,会调用string类的析构,于是ps指向的内存会被释放,并不会导致内存泄漏
return;
}
不是类的数据类型的普通指针,没有后台,就会导致内存泄漏
void remodel(int & i)
{
int * ps = new int;
···
if(weird_thing)
throw exception();
i = *ps;
delete ps;//一旦发生异常,这句代码根本不被执行,导致内存泄漏
return;
}
虽然当时讲栈解退提到了解决方法,但是还是不够好,现在智能指针提供了一个很好的解决办法。
智能指针模板类就是借鉴了string类对象使用析构函数帮自己释放内存这一点,智能指针背后的思想就是基于把常规指针包装为一个有析构函数后台的类对象。
哪些行为类似于常规指针
- 可以对智能指针对象解引用,
*ps
- 可以用它访问结构成员,
ps->data
- 可以把他赋给同类型的常规指针
- 可以把它赋给同类型的智能指针对象
怎么用
首先包含头文件memory
这三个模板类的声明都在memory头文件中。
#include <memory>
智能指针模板也位于名称空间std中
再实例化所需类型的指针
用三个模板类的构造函数
template <class X> class auto_ptr{
public:
explicit auto_ptr(X * p = 0) throw();
//throw(),参数列表为空,表示该函数不会抛出任何异常
//这是异常规范,C++11也将其摒弃了。
}
比如,用double和string类型去实例化,创建指向double和string的智能指针:
auto_ptr<double> pd(new double);//代替double * pd
auto_ptr<string> ps(new string);//代替string * ps
仔细挖掘的话,我们应该想到,上述两句代码中,圆括号里的new double
返回的地址,是auto_ptr<double>
类的构造函数的实际参数,即相当于是上面auto_ptr构造函数中的形参p的实参。
另外两种智能指针的实例化也一样:
unique_ptr<double> pdu(new double);
shared_ptr<double> pds(new double);
注意:不可以把常规指针隐式转换为智能指针,只可以显式转换
double * p_reg = new double;//常规指针
shared_ptr<double> pd;//智能指针
shared_ptr<double> pshared = p_reg;//不允许,隐式转换,复制构造函数
shared_ptr<double> pshared(p_reg);//允许
第二个允许是因为所有三个智能指针模板类都有一个explicit构造函数,这个构造函数的参数是常规指针,这个构造函数的用途是把常规指针显式转换为智能指针
重载的赋值运算符也一样,不接受隐式转换
pd = p_reg;//出错,不允许,因为这是隐式转换
pd = shared_ptr<double> (p_reg);//可以
简单示例1
把上面那个函数直接改了
#include <memory>
void remodel(std::string &str)
{
std::auto_ptr<std::string> ps(new std::string(str));
···
if (weird_thing)
throw exception();
str = *ps;
//delete ps;//不需要了,no longer needed
return;
}
智能指针模板也位于名称空间std中
简单示例2
//hangman.cpp
#include <iostream>
#include <string>
#include <memory>
using std::string;
using std::cout;
using std::cin;
using std::endl;
class Report{
private:
string str;
public:
Report(const string s):str(s)
{
cout << "Object created!\n";}
~Report(){
cout << "Object deleted!\n";}
void comment() const {
cout << str << '\n';}
};
int main()
{
{
std::auto_ptr<Report> ps(new Report