C++11智能指针的常见误区 Top10
2017-05-13 16:41
260 查看
C++11智能指针的常见误区 Top10
我很喜欢C++11全新的智能指针,对于那些苦恼于自己管理内存的开发者来说,这是天大的好事。但是,在广泛使用C++11智能指针的两年里,因为使用方法不当而导致程序的效率下降甚至直接crash掉的事情发生过不知道多少此。为了给大家参考,举出下面的一些例子。误区1:在使用unique_ptr就足够的情况下使用shared_ptr
我最近交接的一些业务中,甚至出现全部对象的生成都是使用shared_ptr,但是分析代码后发现,有大约9成被shared_ptr包裹的资源都不需要也没有被共用。这样做,至少有以下两个问题:
1.程序的某一部分通过另一个共用指针改变额资源,但是当前指针并没有发现资源已经变更,将会导致不可预料的错误。即使另一个共用指针保持指向的资源不变,他可能超乎预料的长时间持有该资源的引用,即使当前指针离开作用域被销毁,该无用资源一直没能被释放;
2.shared_ptr比unique_ptr的生成时间更长,占用资源更多,运行效率更低。因为在shared_ptr的内部多出了引用计数和控制域,而且它们必须是线程安全的。
误区2:以为通过shared_ptr共用的资源/对象是线程安全的
虽然shared_ptr内部的引用计数和控制域都是线程安全的,但对于被shared_ptr管理的多线程共用资源依然需要基本的同步处理才能保证线程安全。误区3:使用auto_ptr
auto_ptr的使用是危险的,当今已经不推荐使用了。使用该指针进行按值传参时会自动调用拷贝构造函数,这会导致整个资源的管理权的转移,这会导致原指针的地址失效,当对它进行取值操作时将直接crash。因为unique_ptr和auto_ptr有相同的作用,而且前者在编写代码时就能自动检测出错误,所以建议将所有使用auto_ptr的地方都替换成unique_ptr。
int main() { auto_ptr<aircraft> myAutoPtr(new Aircraft("F-15")); SetFlightCountWithAutoPtr(myAutoPtr); // Invokes the copy constructor for the auto_ptr myAutoPtr->m_flyCount = 10; // CRASH !!! }
误区4:shared_ptr初始化时并没有使用make_shared
shared_ptr<aircraft> pAircraft(new Aircraft("F-16")); // Two Dynamic Memory allocations - SLOW !!!
如果像上面那样初始化shared_ptr,会有两次动态的内存分配,一次是要作为被管理的新对象本身,另一次是shared_ptr构造函数生成的负责管理的指针对象。
但是C++编译器针对shared_ptr其实可以一次性生成一个较大的内存空间直接存放这两个对象,代码如下所示。
shared_ptr<aircraft> pAircraft = make_shared<aircraft>("F-16"); // Single allocation - FAST !
误区5:生成对象后没有马上将地址分配给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; }
显然,当main函数运行结束时,这两个shared_ptr都会尝试释放同一个对象的内存(计数器变为0),这将会导致程序crash。因此就算你不使用make_shared初始化shared_ptr,也至少保证在生成对象后马上初始化shared_ptr,保证之后绝不用这个原始的指针生成另外的任何智能指针。
误区6:尝试获取shared_ptr所管理的原始指针
使用shared_ptr.get()可以获取shared_ptr管理的原始指针,但这应该极力避免。思考下一下代码。void StartJob() { shared_ptr<aircraft> pAircraft(new Aircraft("F-16")); Aircraft* myAircraft = pAircraft.get(); // returns the raw pointer delete myAircraft; // myAircraft is gone }
crash原因跟误区5如出一辙
误区7:对于指针数组使用shared_ptr的时候不使用自定义的析构函数
思考一下下面的例子void StartJob() { shared_ptr<aircraft> ppAircraft(new Aircraft[3]); }
在这里,shared_ptr只指向了Aircraft[0],而数组的其他两个元素将不会被释放内存,从而造成内存泄露。应该参照下面的写法。
void StartJob() { shared_ptr<aircraft> ppAircraft(new Aircraft[3], [](Aircraft* p) {delete[] p; }); }
误区8:使用shared_ptr时要避免出现循环引用
思考下下面的例子class Aircraft { private: string m_model; public: int m_flyCount; shared_ptr<Aircraft> myWingMan; …
int main() { shared_ptr<aircraft> pMaverick = make_shared<aircraft>("Maverick: F-14"); shared_ptr<aircraft> pIceman = make_shared<aircraft>("Iceman: F-14"); pMaverick->myWingMan = pIceman; // So far so good - no cycles yet pIceman->myWingMan = pMaverick; // now we got a cycle - neither maverick nor goose will ever be destroyed return 0; }
显然,当函数调用结束后,这两个新对象都不会被释放资源。因此当没有确实的必要获取对象的强引用(即保证对象一定要是存活的)的情况下,应该尽量使用weak_ptr,如下。
class Aircraft { private: string m_model; public: int m_flyCount; weak_ptr<Aircraft> myWingMan; …
误区9:没有释放unique_ptr.release()返回的原始指针
因为release()方法并没有把unique_ptr所管理的对象释放掉,只是将unique_ptr和它管理的资源对象解除联系而已,所以需要你手动释放才行。int main() { unique_ptr<aircraft> myAircraft = make_unique<aircraft>("F-22"); Aircraft* rawPtr = myAircraft.release(); return 0; }
显然,myAircraft将一直存活下去。
误区10:调用weak_ptr.lock()时没有确认是否有效
在使用weak_ptr之前,调用它的lock()方法是有必要的,它实际上是获取并替换对应的对象的当前真实的值。但如果这时候这个对象已经被释放掉了,weak_ptr就会变成空指针,如果对它进行取值操作,结果可想而知。class Aircraft { private: string m_model; public: int m_flyCount; weak_ptr<Aircraft> myWingMan; …
int main() { shared_ptr<aircraft> pMaverick = make_shared<aircraft>("F-22"); shared_ptr<aircraft> pIceman = make_shared<aircraft>("F-14"); pMaverick->myWingMan = pIceman; pIceman->m_flyCount = 17; pIceman.reset(); // destroy the object managed by pIceman cout << pMaverick->myWingMan.lock()->m_flyCount << endl; // ACCESS VIOLATION return 0; }
但也不能保证两次lock()之间对象一定不会被释放(第一次取出用来判空,第二次才用来真正访问),正确做法应该是以下这样的。
shared_ptr<aircraft> wingMan = pMaverick->myWingMan.lock(); if (wingMan) { cout << wingMan->m_flyCount << endl; }
相关文章推荐
- c++11:智能指针之shared_ptr
- C++11 FAQ中文版:共享资源的智能指针——shared_ptr
- C++11新特性之智能指针(shared_ptr/unique_ptr/weak_ptr)
- c++11之unique_ptr智能指针使用
- WinDBG技巧:this指针的常见误区 (ECX寄存器存放this指针)
- 【C++11新特性】 C++11智能指针之shared_ptr
- 【C++11新特性】 C++11智能指针之weak_ptr
- [置顶] c++(智能指针 c++11 智能指针)
- C++11新特性之智能指针
- C++11-智能指针-shared_ptr
- (转)C++11里的智能指针
- c++11中的智能指针
- 【C++11新特性】 C++11智能指针之unique_ptr
- c++11之智能指针
- C++11中新增加的智能指针
- C++11 通用智能指针
- 使用 C++11 智能指针时要避开的 10 大错误
- 【C++11新特性】 C++11智能指针之shared_ptr
- 【面向对象程序设计常见面试题】什么叫智能指针?
- C++11 智能指针之 std::shared_ptr 初级学习