使用C++11智能指针时要避开的10大错误

发表于:2016-8-24 11:25

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

 作者:欣仔    来源:51Testing软件测试网采编

#
DoNet
分享:
  我很喜欢新的C++11的智能指针。在很多时候,对很多讨厌自己管理内存的人来说是天赐的礼物。在我看来,C++11的智能指针能使得C++新手教学更简单。
  其实,我已经使用C++11两年多了,我无意中发现多种错误使用C++11智能指针的案例,这些错误会使程序效率很低或者直接崩溃。为了方便查找,我把它们按照下文进行了归类。
  在开始之前,我们用一个简单的Aircraft类来展示一下这些错误。
class Aircraft
{
private:
string m_model;
public:
int m_flyCount;
weak_ptr<aircraft> myWingMan;
void Fly()
{
cout << "Aircraft type" << m_model << "is flying !" << endl;
}
Aircraft(string model)
{
m_model = model;
cout << "Aircraft type " << model << " is created" << endl;
}
Aircraft()
{
m_model = "Generic Model";
cout << "Generic Model Aircraft created." << endl;
}
~Aircraft()
{
cout << "Aircraft type " << m_model << " is destroyed" << endl;
}
};
</aircraft>
  错误#1:当唯一指针够用时却使用了共享指针
  我最近在一个继承的代码库项目中工作,它使用了一个shared_ptr(译者注:共享指针)创建和管理所有的对象。我分析了这些代码,发现在90%的案例中,被shared_ptr管理的资源并非是共享的。
  有两个理由可以指出这是错误的:
  1、如果你真的需要使用独有的资源(对象),使用shared_ptr而不是unique_ptr会使你的代码容易出现资源泄露和一些bug。
  不易察觉的bug:有没有想过这种情况,如果有其他程序员无意间通过赋值给另一个共享指针而修改了你共享出来的资源/对象,而你却从没有预料到这种事情!
  不必要的资源使用:即使其他的指针不会修改你的对象资源,但也可能会过长时间地占用你的内存,甚至已经超出了原始shared_ptr的作用范围。
  2、创建shared_ptr比创建unique_ptr更加资源密集。
  shared_ptr需要维护一个指向动态内存对象的线程安全的引用计数器以及背后的一个控制块,这使它比unique_ptr更加复杂。
  建议 – 默认情况下,你应该使用unique_ptr。如果接下来有共享这个对象所有权的需求,你依然可以把它变成一个shared_ptr。
  错误#2:没有保证shared_ptr共享的资源/对象的线程安全性!
  Shared_ptr可以让你通过多个指针来共享资源,这些指针自然可以用于多线程。有些人想当然地认为用一个shared_ptr来指向一个对象就一定是线程安全的,这是错误的。你仍然有责任使用一些同步原语来保证被shared_ptr管理的共享对象是线程安全的。
  建议– 如果你没有打算在多个线程之间来共享资源的话,那么就请使用unique_ptr。
  错误#3:使用auto_ptr!
  auto_ptr的特性非常危险,并且现在已经被弃用了。当该指针被当作参数进行值传递时会被拷贝构造函数转移所有权,那么当原始auto指针被再次引用时就会造成系统致命的崩溃。看看下面这个例子:
  int main()
  {
  auto_ptr<aircraft> myAutoPtr(new Aircraft("F-15"));
  SetFlightCountWithAutoPtr(myAutoPtr); // Invokes the copy constructor for the auto_ptr
  myAutoPtr->m_flyCount = 10; // <span style="color: #ff0000;">CRASH !!!</span>
  }
  </aircraft>
  建议 – unique_ptr可以实现auto_ptr的所有功能。你应该搜索你的代码库,然后找到其中所有使用auto_ptr的地方,将其替换成unique_ptr。最后别忘了重新测试一下你的代码!
  错误#4:没有使用make_shared来初始化shared_ptr!
  相较于使用裸指针,make_share有两个独特的优点:
  1.性能: 当你用new创建一个对象的同时创建一个shared_ptr时,这时会发生两次动态申请内存:一次是给使用new申请的对象本身的,而另一次则是由shared_ptr的构造函数引发的为资源管理对象分配的。
  shared_ptr<aircraft> pAircraft(new Aircraft("F-16")); // Two Dynamic Memory allocations - SLOW !!!
  </aircraft>
  与此相反,当你使用make_shared的时候,C++编译器只会一次性分配一个足够大的内存,用来保存这个资源管理者和这个新建对象。
  shared_ptr<aircraft> pAircraft = make_shared<aircraft>("F-16"); // Single allocation - FAST !
  </aircraft></aircraft>
  2、在看了MS编译器的memory头文件实现以后,我发现当内存分配失败时,这个对象就会被删除掉。这样的话使用裸指针初始化也不用担心安全问题了。
  建议- 使用make_shared而不是裸指针来初始化共享指针。
  错误#5:在创建一个对象(裸指针)时没有立即把它赋给shared_ptr。
  一个对象应该在被创建的时候就立即被赋给shared_ptr。裸指针永远不应该被再次使用。
  看看下面则个例子:
int main()
{
Aircraft* myAircraft = new Aircraft("F-16");
shared_ptr<aircraft> pAircraft(myAircraft);
cout << pAircraft.use_count() << endl; // ref-count is 1
shared_ptr<aircraft> pAircraft2(myAircraft);
cout << pAircraft2.use_count() << endl; // ref-count is 1
return 0;
}
</aircraft>
  这将会造成ACCESS VIOLATION(译者注:非法访问)并导致程序崩溃!!!
  这样做的问题是当第一个shared_ptr超出作用域时,myAircraft对象就会被销毁,当第二个shared_ptr超出作用域时,程序就会再次尝试销毁这个已经被销毁了的对象!
  建议– 如果不使用make_shared创建shared_ptr,至少应该像下面这段代码一样创建使用智能指针管理的对象:
  shared_ptr<aircraft> pAircraft(new Aircraft("F-16"));
  </aircraft>
21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

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

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

沪ICP备05003035号

沪公网安备 31010102002173号