定义:智能指针是行为类似指针的类对象,但这种对象还有一些其他的功能。
为什么要有智能指针?
🌰案例说明1:
void test(std::string & str){
std::string * ps = new std::string(str);
return ;
}
通过以上案例可以发现,在函数内new了一个string对象后并没有进行delete操作,当我们大量地调用test()函数时会造成大量的内存泄漏。这种情况可能是因为我们忘了在return前加上delete ps;这句话,但是如果我们总是记住加delete但仍会出现一些意想不到的情况导致没有执行delete从发引发内存泄漏,如下:
🌰案例说明2:
void test(std::string & str){
std::string * ps = new std::string(str);
if(weird_thing()))
throw exception();
delete ps;
return ;
}
从以上案例我们可以发现当出现异常时,delete将不被执行,因此同样会导致内存泄漏。
因此,我们想如果ps有一个析构函数,在ps过期时会及时释放它指向的内存这该多好啊,但问题是ps仅是一个常规指针,不是有析构函数的类对象,因此就有了auto_ptr、unique_ptr、shared_ptr等类似指针的类对象的出现。
#include <memory>
void test(std::string & str){
std::auto_ptr(std::string) ps (new std::string(str));
if(weird_thing()))
throw exception();
return ;
}
以上是将普通的ps指针替换成auto_ptr智能指针的案例,我们可以省去delete操作,并且可以确保在ps指针在生命周期结束后自动调用析构函数释放内存,避免内存泄漏。
智能指针的特性:
- 智能指针对象的很多方面都类似于常规指针。例如,如果ps是一个智能指针对象,则可以对它执行解除引用操作(*ps)、用它访问结构成员(ps->puffIndex)、将它赋给指向同类型的常规指针等。
1.为什么要放弃auto_ptr?
auto_ptr<string> ps(new string("I love shushu."));
auto_ptr<string> m_ps;
m_ps = ps;
如果ps与m_ps都是常规指针,则两个指针将指向同一个string对象,这是显然不可以接受的,因为当程序将试图删除同一个对象两次,当然避免的方法也有很多种:
- 定义赋值运算符,使其执行深拷贝,这样两个指针将指向不同的对象,其中的一个对象是另一个对象的副本。
- 建立所有权概念,对于特定的丢向,只能有一个智能指针可以拥有它,这样只有拥有对象的智能指针的构造函数会删除该对象。然后让赋值操作转让所有权,这就是用于auto_ptr和unique_ptr的策略。
- 创建更加智能的指针类对象,跟踪引用特定对象的智能指针数。这称为引用计数,例如每当赋值时,计数将加1,而指针国企时,计数将减1.仅当最后一个指针过期时,才调用delete。这就是shared_ptr采用的策略。
以下案例将不适用于auto_ptr:
void test(){
auto_ptr<string> fruit[3] = {
auto_ptr<string> (new string("apple")),
auto_ptr<string> (new string("banana")),
auto_ptr<string> (new string("orange"))
};
auto_ptr<string> pf;
pf = fruit[2];//此时fruit[2]将丧失所有权,转移给pf
for(int i = 0; i < 3; i++)
cout<<*fruit[i]<<endl;
}
以上代码的循环输出仅会输出:apple banana,然后就会提示Sementation fault(core dumped)错误。这是因为fruit[2]将所有权转让后,仍用它来访问该对象,却发现此时已经是一个空指针了。但如果将智能指针替换成shared_ptr即可解决该问题。因为shared_ptr采用引用计数,当pf与fruit[2]指向一个对象时,引用计数从1增加到2,因此会使pf先调用析构函数,将引用计数降低到1,然后将shared_ptr数组的成员书房,对fruit[2]调用析构函数,再将引用计数降为0,释放所分配的空间。
2.为什么unique_ptr优于auto_ptr?
根据上面的代码我们可知,当pf接管所有权后,如果程序随后再试图使用fruit[2]则会报错,因为pf无法指向有效的数据。但如果把auto_ptr换成unique_ptr后,pf仍会接管所有权,但在编译过程中,编译器就会认定pf=fruit[2]是非法的,从而避免了fruit[2]不再指向有效数据的问题。因此unique_ptr比auto_ptr更安全主要体现在编译阶段错误比潜在的程序崩溃更安全。
(warning:unique_ptr却允许赋给一个临时右值。)
unique_ptr还有另一个优点:它拥有一个可用于数组的变体,即有使用new[]和delete[]的版本。而auto_ptr和shared_ptr仅能使用New分配内存,不可用new[]。
参考文献
《Effective C++(第三版)》