智能指针
前言:此文章是本菜鸟对C++ Primer Plus 中描述智能指针部分的理解。有错误的地方请慷慨相告,在下感激不尽。
定义
智能指针是行为类似于指针的类对象。
为什么使用智能指针
#include <iostream>
#include <string>
using namespace std;
void fun(string &str);
int main()
{
string str;
int i=1000;
while(i>0){
fun(str);
i--;
}
system("pause");
return 0;
}
void fun(string &str)
{
string *ptr=new string (str);
//一系列过程;
str=*ptr;
return ;
}
可以看出此代码中有缺陷,在fun函数中,分配了堆中的内存,但是从来不收回。在主函数中一直调用fun函数,从而导致内存泄漏。
解决方法,在return前记得使用delete将释放ptr。但凡是别忘了,都有可能会忘记,还有一种情况。
将fun函数中添加一段异常处理代码
void fun(string &str)
{
string *ptr=new string (str);
//一系列过程;
if(weird_thing())
{
throw exception();
}
str=*ptr;
delete ptr;
return ;
}
当出现异常时,delete语句没有被执行,依然造成内存泄露。
方法一:可以在引发异常的函数中捕获该异常,在catch块中包含一些清理代码,然后重新引发异常。
void fun(string &str)
{
string *ptr=new string (str);
//一系列过程;
try{
if(weird_thing())
{
throw exception();
}
}
catch (exception &ex)
{
delete ptr;
throw;
}
str=*ptr;
delete ptr;
return ;
}
但是,会增加疏忽和产生其他错误的机会。所以使用智能指针解决会比较优雅。
在fun函数终止时(异常终止和正常终止),本地变量从栈内存中删除,ptr指针会被释放。
为了释放ptr所指向的内存,可以使ptr成为类对象,在类的析构器中释放它所指向的内存,这正是auto_ptr、unique_ptr、shared_ptr的思想。
如何让使用智能指针
1.添加头文件memory
2.将指向string的指针换成string的智能指针对象。
3.删除delete
//需要添加头文件 memory
void fun(string &str)
{
std::auto_ptr<std::string> ptr(new std::string (str)); //注意智能指针在std命名空间中
//一系列过程;
if(weird_thing())
{
throw exception();
}
str=*ptr;
//delete ptr;
return ;
}
值得注意智能指针类的构造函数是explicit修饰的,该构造函数以指针作为参数,因此不需要自动将指针转换为智能指针对象,explicit防止了隐式转换。
注意事项
1.避免程序将delete运算符用于非堆内存。
string a1("abcde");
shared_ptr<string> p(&a1);
在代码块执行结束时,p过期,会调用delete释放了非堆内存。因此造成错误。
2.指针之间的赋值
auto_ptr<string> p(new string ("abcd"));
auto_ptr<string> a;
a=p;
此赋值语句中,如果p和a是常规的指针时,p和a指向同一个string的对象,这是不能接受的,因为p过期的string对象被删除,a过期时string对象又被删除。解决方法:
一、重载赋值运算符,使赋值时,两个指针不是指向同一个对象,一个指针对象指向另一个指针对象的副本。
二、建立所有权概念(ownership),对于特定的对象,只能有一个智能指向它,这样只有拥有对象的智能指针的析构函数会删除该对象。然后让赋值操作转让所有权,这就是用于auto_ptr和unique_ptr的策略,当然unique_ptr更严格。
三、创建智能更高指针,跟踪特定对象的智能指针数(引用计数),例如,赋值时,计数加一,指针过期时,计数减一。当最后一个指针过期时,才调用delete,这就是shared_ptr采用的策略。
总结
对于智能指针之间的区别,以及如何让选择智能指针。下此再看…