本文章所有内容源于《C++Primer Plus第六版》16.2节,帮您快速理解使用智能指针
常规指针基础知识:
指针的作用:在写代码调用大量数据的时候,如果将整个数据块拿过来用,这样十分耗费内存与运行时间。指针只调用需要的数据块内存的首地址即可,只要知道了该内存首地址,就可以访问整个内存块以及它上面的数据。
堆区:在使用指针的时候,程序员需要在堆区,使用“new”创建一个堆并占据内存,并在指针使用结束后,使用“delete”删除占用的内存空间。
#include <iostream>
#include <string>
#include <memory>
using namespace std;
//常规指针
class Report
{
private:
int number;
public:
Report()
{
cout << "构造函数!" << endl;
number = 5;
}
~Report()
{
cout << "析构函数!" << endl;
}
void test( )
{
cout << number << endl;
}
};
int main()
{
cout << "常规数据指针:" << endl;
int* p = new int ;
*p = 500;
cout << "*p = " << *p << endl;
cout << p << endl;
delete p;
cout << "-------------------------------------"<< endl;
cout << "常规类指针:" << endl;
Report* m = new Report;
cout << "number = " ;
m->test();
cout << m << endl;
delete m;
return 0;
}
上述代码展示了指针在使用时的基础用法,一个是指向一个数,另一个是指向一个类。需要记住的是:
- 指针的定义方法:
类型* 名称 = new 类型;
这里的类型肯能是int、double,也可能是创建的类。
- 指针释放:
delete 名称;
指针如果不释放就会一直占用堆区内存空间,当堆区内存满了,程序就会崩溃,所以使用结束后必须释放指针。
可是问题就在于在维护大段代码时,经常会用到指针间的互相调用。如果手动释放指针,可能会出现:把指针A释放了,结果一开始被指针A赋值的指针B不能用了。因此我们引出智能指针的概念。
- 地址:
指针指向的是地址,代码中p就是内存中的地址,*p成为解引用指向该地址代表的数据
cout << p << endl;
智能指针基础知识:
智能指针作用:类似于指针的类对象,简单说就是在既可以动态分配内存(在堆区new内存),也可以在代码块(一个“{}”)结束后,自动释放内存。就不再需要程序员手动对指针进行释放,防止内存泄漏。
分类:shared_ptr、unique_ptr、auto_ptr
#include <iostream>
#include <string>
#include <memory>
using namespace std;
//智能指针模板类
class Report
{
private:
std::string str;
public:
Report( const std::string s ) : str(s)
{
std::cout << " Object created! \n " ;
}
~Report()
{
std::cout << "Object delected! \n" ;
}
void comment() const
{
std::cout << str << "\n" ; //常成员函数是指不能改变成员变量值的函数
}
};
int main()
{
{
std::auto_ptr<Report> ps ( new Report("using auto_ptr") );
ps->comment();
}
cout << "--------------------------"<<endl;
{
std::unique_ptr<Report> ps( new Report("using shared_ptr") );
ps->comment();
}
cout << "--------------------------"<<endl;
{
std::shared_ptr<Report> ps( new Report("using shared_ptr") );
ps->comment();
}
cout << "--------------------------"<<endl;
{
std::shared_ptr<Report> pt = std::shared_ptr<Report> ( new Report("shared_ptr") );
pt->comment();
}
cout << "--------------------------"<<endl;
return 0;
}
上述代码首先创建一个Report类,定义它的构造函数、析构函数,定义一个comment函数用于接收构造函数的字符串常量(为了便于观察)。接着在main函数中使用了三种智能指针模板类。需要记住的是:
- 智能指针定义方法:
std::智能指针类型<类型> 名称( new 构造函数 )
智能指针类型就是:shared_ptr、unique_ptr、auto_ptr
类型:类名
名称:自己创建,常用Ptr
- 智能指针调用:
名称->成员函数;
成员函数在类中定义,注意指针的使用时候成员函数使用“->”。
(没有指针时,成员函数调用为“名称.成员函数”)
- 智能指针赋值:
std::智能指针类型<类型> 名称 = std::智能指针类型<类型> (new 构造函数);
举两个个例子:
对于类:
std::shared_ptr<Report> pt = std::shared_ptr<Report> ( new Report("shared_ptr") );
对于string:
std::shared_ptr <string> films[5] =
{
shared_ptr<string> (new string ("Fowl Balls")),
shared_ptr<string> (new string ("Duck Walls")),
shared_ptr<string>(new string("Chicken Runs")),
shared_ptr<string>(new string("Turkey Errors")),
shared_ptr<string>(new string("Goose Eggs"))
}
智能指针使用问题:
博主在学习《视觉slam十四讲》时遇到的全都是shared_ptr,所以可想它的地位。
在使用智能指针的时候,也会出现一个智能指针的数据赋值给另一个智能指针,这里我们指类型不变,例如(unique_ptr类型的智能指针赋值给unique_ptr类型的智能指针),直接上例子:
#include <iostream>
#include <string>
#include <memory>
using namespace std;
//该文件用于证明auto_ptr不是一个好的选择
int main()
{
//unique_ptr
std::unique_ptr <string> films[5] =
{
unique_ptr<string> (new string ("Fowl Balls")),
unique_ptr<string> (new string ("Duck Walls")),
unique_ptr<string>(new string("Chicken Runs")),
unique_ptr<string>(new string("Turkey Errors")),
unique_ptr<string>(new string("Goose Eggs"))
};
unique_ptr<string> pwin;
pwin = films[2];//films[2]失去所有权
/*
//auto_ptr
std::auto_ptr <string> films[5] =
{
auto_ptr<string> (new string ("Fowl Balls")),
auto_ptr<string> (new string ("Duck Walls")),
auto_ptr<string>(new string("Chicken Runs")),
auto_ptr<string>(new string("Turkey Errors")),
auto_ptr<string>(new string("Goose Eggs"))
};
auto_ptr<string> pwin;
pwin = films[2];//films[2]失去所有权
*/
/*
//shared_ptr 唯一能用
std::shared_ptr <string> films[5] =
{
shared_ptr<string> (new string ("Fowl Balls")),
shared_ptr<string> (new string ("Duck Walls")),
shared_ptr<string>(new string("Chicken Runs")),
shared_ptr<string>(new string("Turkey Errors")),
shared_ptr<string>(new string("Goose Eggs"))
};
shared_ptr<string> pwin;
pwin = films[2];//films[2]失去所有权
*/
cout << "最佳鸟类棒球电影的提名者是:"<<endl;
for ( int i = 0 ; i < 5 ; i++ )
{
cout << *films[i] << endl; //指针所以要解引用
}
cout << "获胜者是:" << *pwin <<endl;
return 0;
}
将代码运行会报错,问题出在当使用unique_ptr时,films[2]已经被提出来给了pwin,那么film[2]现在就是个空指针,所以无法运行。读者可以取消其他的注释,尝试用shared_ptr或者auto_ptr,最终发现只有shared_ptr可以正常跑通过。
注意:
- shared_ptr在赋值使用后,指针计数器从1变成了2,读者可以理解为系统知道你又指向了一个和它一样的数据,那么在释放的时候,从2至1挨个释放(即1->2->1->0)。而另外两种在赋值时,指针计数器始终为1,可以理解为第一个数据块指针从1变成了0,没了,然后新赋值的指针从0变成了1(即1->0,0->1->0)。
- 使用new分配内存时,才能使用auto_ptr和shared_ptr,使用new []分配内存,不能使用它们。不是用new分配内存时,不能使用auto_ptr或shared_ptr;不是用new或new []分配内存时,不能使用unique_ptr。