编写高质量代码:改善C++程序的150个建议(连载18)

发表于:2012-4-27 09:40

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:李健    来源:51Testing软件测试网采编

  建议34:用智能指针管理通过new创建的对象

  前面的建议中我们不厌其烦的一再重复:内存泄漏是一个很大很大的问题!为了应对这个问题,已经有许多技术被研究出来,比如Garbage Collection(垃圾回收)、Smart Pointer(智能指针)等。Garbage Collection技术一直颇受注目,并且在Java中已经发展成熟,成为内存管理的一大利器,但它在C++语言中的发展却不顺利,C++为了追求运行速度,20年来态度坚决地将其排除在标准之外。真不知C++通过加大开发难度来换取执行速度的做法究竟是利还是弊。为了稍许平复因为没有Garbage Collection而引发的C++程序员的怨气,C++对Smart Pointer技术采取了不同的态度,它选择对这一技术的支持,并在STL中包含了支持Smart Pointer技术的class,赐予了C/C++程序员们一件管理内存的神器。

  Smart Pointer是Stroustrup博士所推崇的RAII(Resource Acquisition In Initialization)的最好体现。该方法使用一个指针类来代表对资源的管理逻辑,并将指向资源的句柄(指针或引用)通过构造函数传递给该类。当离开当前范围(scope)时,该对象的析构函数一定会被调用,所以嵌在析构函数中的资源回收的代码也总是会被执行。这种方法的好处在于,由于将资源回收的逻辑通过特定的类从原代码中剥离出来,自动正确地销毁动态分配的对象,这会让思路变得更加清晰,同时确保内存不发生泄露。

  它的一种通用实现技术是使用引用计数(Reference Count)。引用计数智能指针,是一种生命期受管的对象,其内部有一个引用计数器。当内部引用计数为零时,这些对象会自动销毁自身的智能指针类。每次创建类的新对象时,会初始化指针并将引用计数置为1;当对象作为另一对象的副本而创建时,它会调用拷贝构造函数拷贝指针并增加与之相应的引用计数;对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数;如果引用计数减至0,则删除对象,并增加右操作数所指对象的引用计数;调用析构函数时,构造函数减少引用计数,直到计数为0,释放对象空间。

  Smart Pointer具有非常强大的能力,谨慎而明智的选择能给我们带来极大的便利。前面已经说到STL中包含了支持Smart Pointer技术的class,它就是智能指针:auto_ptr。要使用auto_prt,首先要包含memory头文件:

#include <memory>

  auto_ptr可以指向一个以new建立的对象,当auto_ptr的生命周期结束时,其所指向的对象之资源也会被自动释放,且不必显式地调用delete,而对象指针的操作依旧如故。例如:

  1. class A  
  2. {  
  3. public:  
  4.      A(){}  
  5.      ~A(){}  
  6.      void Hello()  
  7.     {  
  8.       std::cout<<"Hello Smart Pointer";  
  9.   }  
  10. };  
  11. int main()  
  12. {  
  13.      std::auto_ptr<A> pA(new A());  
  14.      pA->Hello();  
  15.      return 0;  
  16. }

  当然,也可以建立一个未指向任何对象的auto_prt,例如:

std::auto_ptr<int> iPtr;

  它就像空指针,未指向任何对象,所以也就不能进行操作,但是可以通过get()函数来判断它是否指向对象的地址:

  1. if(iPtr.get() == 0) // 不指向任何对象  
  2. {  
  3.    iPtr.reset(new int(2011)); // 指向一个对象  
  4. }  
  5. auto_ptr还可以使用另一个auto_ptr来建立,但是需要十分小心的是,这会造成所有权的转移,例如:  
  6. auto_ptr< string> sPtr1 (new string("Smart Pointer"));  
  7. auto_ptr< string> sPtr2 (sPtr1);  
  8. if( !sPtr1->empty() )  
  9.  cout<<*sPtr1<< endl;

  当使用sPtr1来建立sPtr2时,sPtr1不再对所指向对象的资源释放负责,而是将接力棒传递到了sPtr2的手里,sPtr1丧失了使用string类成员函数的权利,所以在判断sPtr1->empty()时程序会崩溃。

  auto_ptr的资源维护动作是以inline的方式来完成的,在编译时代码会被扩展开来,所以使用它并不会牺牲效率。虽然auto_ptr指针是一个RAII对象,能够给我们带来很多便利,但是它的缺点同样不可小觑:

  auto_ptr对象不可作为STL容器的元素,所以二者带来的便利不能同时拥有。这一重大缺陷让STL的忠实拥趸们愤怒不已。

  auto_ptr缺少对动态配置而来的数组的支持,如果用它来管理这些数组,结果是可怕的、不可预期的。

  auto_ptr在被复制的时候会发生所有权转移。

41/41234>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号