I love the new C++ 11 smart pointers. In many ways, they were a godsent for many folks who hate managing their own memory. In my opinion, it made teaching C++ to newcomers much easier.
我喜欢新的C ++ 11智能指针。在很多方面,他们是为godsent恨谁管理自己的记忆很多人。在我看来,它使教学C ++新人容易得多。
However, in the two plus years that I've been using them extensively, I've come across multiple cases where improper use of the C++ 11 smart pointers made the program inefficient or simply crash and burn. I've catalogued them below for easy reference.
然而,在我使用它们广泛地被两个多年,我已经遇到许多情况下,不当使用C ++ 11智能指针所生成的程序效率低下或者干脆和好如初。我下面编目他们,以供参考。
Before we begin, let's take a look at a simple Aircraft class we'll use to illustrate the mistakes.
在我们开始之前,让我们来看看,我们将用它来说明错误,一个简单的飞机类。
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> http://blog.csdn.net/sergeycao/article/details/52538123 曹纪乾曹浩洋的专栏个人主页|我的博客Mistake # 1 : Using a shared pointer where an unique pointer suffices !!!
错误#1:使用共享指针,其中一个唯一的指针就足够了!
I’ve recently been working in an inherited codebase which uses a shared_ptr for creating and managing every object. When I analyzed the code, I found that in 90% of the cases, the resource wrapped by the shared_ptr is not shared.
我最近一直在使用,用于创建和管理每个对象一个shared_ptr继承的代码库工作。当我分析的代码,我发现,在90%的情况,由shared_ptr的包装资源不共享。
This is problematic because of two reasons:
1. If you have a resource that’s really meant to be owned exclusively, using a shared_ptr instead of a unique_ptr makes the code susceptible to unwanted resource leaks and bugs.
Subtle Bugs: Just imagine if you never imagined a scenario where the resource is shared out by some other programmer by assigning it to another shared pointer which inadvertently modifies the resource ! Unnecessary Resource Utilization: Even if the other pointer does not modify the shared resource, it might hang on to it far longer than necessary thereby hogging your RAM unnecessarily even after the original shared_ptr goes out of scope.2. Creating a shared_ptr is more resource intensive than creating a unique_ptr.
A shared_ptr needs to maintain the threadsafe refcount of objects it points to and a control block under the covers which makes it more heavyweight than an unique_ptr. 这是由于两方面的原因存在问题: 1.如果您有这确实意味着使用一个shared_ptr代替的unique_ptr使得代码容易受到不必要的资源泄漏和错误应该只拥有一个资源。 微妙的错误:试想一下,如果你从来没有想过当资源被其他程序员共享出来的情景将其分配到另一个共享指针,无意中修改资源! 不必要的资源利用率:即使其他指针不修改共享资源,它可能更长的时间挂在比必要占用,从而你的内存不必要原来的shared_ptr超出范围后还是一样。 2.创建一个shared_ptr更多的资源比创建的unique_ptr密集。 一个shared_ptr需要维持,这使得它比的unique_ptr更重量级的幕后对象的引用计数线程它指向和控制模块。 建议 - 默认情况下,你应该使用的unique_ptr。如果要求上来以后共享资源所有权,可以随时将其更改为一个shared_ptr。Recommendation – By default, you should use a unique_ptr. If a requirement comes up later to share the resource ownership, you can always change it to a shared_ptr.
http://blog.csdn.net/sergeycao/article/details/52538123 曹纪乾曹浩洋的专栏个人主页|我的博客
Mistake # 2 : Not making resources/objects shared by shared_ptr threadsafe !
错误#2:不使由shared_ptr的线程共享资源/对象!
Shared_ptr allows you to share the resource thorough multiple pointers which can essentially be used from multiple threads. It’s a common mistake to assume that wrapping an object up in a shared_ptr makes it inherently thread safe. It’s still your responsibility to put synchronization primitives around the shared resource managed by a shared_ptr.
shared_ptr的可以让你分享这基本上可以从多个线程使用的资源彻底多个指针。这是一个常见的错误假设,在一个shared_ptr的包装对象多达使得它固有线程安全的。它仍然是你的责任,把周围由一个shared_ptr管理的共享资源同步原语。 建议 - 如果你不共享多个线程之间的资源规划,使用的unique_ptr。
Recommendation – If you do not plan on sharing the resource between multiple threads, use a unique_ptr.
http://blog.csdn.net/sergeycao/article/details/52538123 曹纪乾曹浩洋的专栏个人主页|我的博客
Mistake # 3 : Using auto_ptr !
错误#3:使用auto_ptr的!
The auto_ptr feature was outright dangerous and has now been deprecated. The transfer of ownership executed by the copy constructor when the pointer is passed by value can cause fatal crashes in the system when the original auto pointer gets dereferenced again. Consider an example:
auto_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 !!! } </aircraft>Recommendation – unique_ptr does what auto_ptr was intended to do. You should do a search and find on your codebase and replace all auto_ptr with unique_ptr. This is pretty safe but don’t forget to retest your code !
建议 - unique_ptr做什么的auto_ptr是为了做。你应该做一个搜索,并找到你的代码,并更换所有的unique_ptr auto_ptr的。这是相当安全的,但不要忘了重新测试你的代码!
曹纪乾曹浩洋的专栏 个人主页|我的博客 http://blog.csdn.net/sergeycao/article/details/52538123 曹纪乾曹浩洋的专栏个人主页|我的博客
1. Performance : When you create an object with new , and then create a shared_ptr , there are two dynamic memory allocations that happen : one for the object itself from the new, and then a second for the manager object created by the shared_ptr constructor.
make_shared比使用原始指针两个明显的优势: 1.性能:当您创建新的对象,然后创建一个shared_ptr,有这种情况发生两个动态内存分配:一个是来自新对象本身,再进行第二次由shared_ptr的构造函数创建的管理器对象。
shared_ptr<aircraft> pAircraft(new Aircraft("F-16")); // Two Dynamic Memory allocations - SLOW !!! </aircraft>On the contrary, when you use make_shared, C++ compiler does a single memory allocation big enough to hold both the manager object and the new object.
相反,当你使用make_shared,C ++编译器的单一内存分配大到足以容纳两个管理对象和新对象。
shared_ptr<aircraft> pAircraft = make_shared<aircraft>("F-16"); // Single allocation - FAST ! </aircraft></aircraft>2. Safety: Consider the situation where the Aircraft object is created and then for some reason the shared pointer fails to be created. In this case, the Aircraft object will not be deleted and will cause memory leak ! After looking at the implementation in MS compiler memory header I found that if the allocation fails, the resource/object is deleted. So Safety is no longer a concern for this type of usage.
2.安全:考虑在其中创建了飞机对象,然后由于某种原因,共享指针创建失败的情况。在这种情况下,飞机的对象将不被删除,并且将导致内存泄漏!看着在MS编译器内存头实施后,我发现,如果分配失败,资源/对象被删除。因此,安全不再是这种类型的用法的关注。
Recommendation: Use make_shared to instantiate shared pointers instead of using the raw pointer.
建议:使用make_shared实例,而不是使用原始指针共享指针。
An object should be assigned to a shared_ptr as soon as it is created. The raw pointer should never be used again.
一个对象应尽快,因为它是建立分配给一个shared_ptr。原始指针不应该被再次使用。
Consider the following example:
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></aircraft>It’ll cause an ACCESS VIOLATION and crash the program !!!
The problem is that when the first shared_ptr goes out of scope, the myAircraft object is destroyed. When the second shared_ptr goes out of scope , it tries to destroy the previously destroyed object again !
它会导致访问冲突和程序崩溃! 问题是,当第一的shared_ptr超出范围,该myAircraft对象被销毁。当第二的shared_ptr超出范围,它会尝试再次摧毁以前销毁的对象!
Recommendation: If you’re not using make_shared to create the shared_ptr , at least create the object managed by the smart pointer in the same line of code – like :
建议:如果你不使用make_shared创建shared_ptr的,至少建立在相同的代码行的智能指针管理的对象 - 这样的:
shared_ptr<aircraft> pAircraft(new Aircraft("F-16")); </aircraft>Mistake # 6 : Deleting the raw pointer used by the shared_ptr !
错误#6:删除由shared_ptr的使用原始指针!
You can get a handle to the raw pointer from a shared_ptr using the shared_ptr.get() api. However, this is risky and should be avoided. Consider the following piece of code:
您可以使用shared_ptr.get()API一个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 } </aircraft>Once we get the raw pointer (myAircraft) from the shared pointer, we delete it. However, once the function ends, the shared_ptr pAircraft goes out of scope and tries to delete the myAircraft object which has already been deleted. The result is an all too familiar ACCESS VIOLATION !
一旦我们从共享指针的原始指针(myAircraft),我们将其删除。但是,一旦函数结束时,shared_ptr的pAircraft超出范围,并试图删除已经被删除的myAircraft对象。其结果是一个再熟悉不过的访问冲突! 建议:想想真的很难,你从共享指针拔出前的原始指针,并挂在了。你永远不知道什么时候有人会打电话给删除原始指针,使你的shared_ptr来访问违反。
Recommendation: Think really hard before you pull out the raw pointer from the shared pointer and hang on to it. You never know when someone is going to call delete on the raw pointer and cause your shared_ptr to Access Violate.
曹纪乾曹浩洋的专栏 个人主页|我的博客 http://blog.csdn.net/sergeycao/article/details/52538123 曹纪乾曹浩洋的专栏个人主页|我的博客
Mistake # 7 : Not using a custom deleter when using an array of pointers with a shared_ptr !
错误#7:使用带有一个shared_ptr指针数组时不使用定制删除!
Consider the following piece of code:
void StartJob() { shared_ptr<aircraft> ppAircraft(new Aircraft[3]); } </aircraft>The shared pointer will just point to Aircraft[0] — Aircraft[1] and Aircraft[2] have memory leaks will not be cleaned up when the smart pointer goes out of scope. If you’re using Visual Studio 2015, you’ll get a heap corruption error.
共享指针将指向刚才飞机[0] - 飞机[1]和飞机[2]有当智能指针超出范围内存泄漏不会被清理。如果你正在使用Visual Studio 2015,你会得到一个堆损坏错误。 建议:经常通过与shared_ptr的管理对象数组的自定义删除。下面的代码修复该问题:
Recommendation: Always pass a custom delete with array objects managed by shared_ptr. The following code fixes the issue:
void StartJob() { shared_ptr<aircraft> ppAircraft(new Aircraft[3], [](Aircraft* p) {delete[] p; }); } </aircraft>Mistake # 8 : Not avoiding cyclic references when using shared pointers !
错误#8:使用共享指针时不避免循环引用!
In many situations , when a class contains a shared_ptr reference , you can get into cyclical references. Consider the following scenario – we want to create two Aircraft objects – one flown my Maverick and one flown by Iceman ( I could not help myself from using the TopGun reference !!! ). Both maverick and Iceman needs to hold a reference to each Other Wingman.
在许多情况下,当一个类包含一个shared_ptr参考,你可以进入循环引用。请考虑以下情形 - 我们要创建两个飞行器的对象 - 一个飞行我特立独行,一个由冰人飞行(我控制不住自己使用该TOPGUN参考!)。无论特立独行和冰人需要持有对方僚机的参考。
So our initial design introduced a self referencial shared_ptr inside the Aircraft class:
因此,我们最初的设计推出了自主的借鉴飞机类中shared_ptr的:
class Aircraft { private: string m_model; public: int m_flyCount; shared_ptr<Aircraft> myWingMan; ….
Then in our main() , we create Aircraft objects, Maverick and Goose , and make them each other’s wingman:
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; } </aircraft></aircraft></aircraft></aircraft>
When main() returns, we expect the two shared pointers to be destroyed – but neither is because they contain cyclical references to one another. Even though the smart pointers themselves gets cleaned from the stack, the objects holding each other references keeps both the objects alive.
当main()返回时,我们预计这两个共享指针被摧毁 - 但也不是因为它们含有彼此循环引用。尽管智能指针本身会从堆栈中清洗,保持相互之间的引用的对象保持两个对象活着。
Here's the output of running the program:
Aircraft type Maverick: F-14 is created
Aircraft type Iceman: F-14 is created
So what’s the fix ? we can change the shared_ptr inside the Aircraft class to a weak_ptr ! Here’s the output after re-executing the main().那么,有什么解决?我们可以在飞机类中的shared_ptr更改为weak_ptr的!后输出的重新执行的main():
Aircraft type Maverick: F-14 is created
Aircraft type Iceman: F-14 is created
Aircraft type Iceman: F-14 is destroyed
Aircraft type Maverick: F-14 is destroyed
Notice how both the Aircraft objects were destroyed.
Recommendation: Consider using weak_ptr in your class design when ownership of the resource is not needed and you don’t want to dictate the lifetime of the object.
建议:考虑是不是需要资源的所有权时,在你的类设计中使用的weak_ptr,你不想决定了对象的生命周期。
曹纪乾曹浩洋的专栏 个人主页|我的博客 http://blog.csdn.net/sergeycao/article/details/52538123 曹纪乾曹浩洋的专栏个人主页|我的博客
The Release() method does not destroy the object managed by the unique_ptr, but the unique_ptr object is released from the responsibility of deleting the object. Someone else (YOU!) must delete this object manually.
Release()方法不破坏由的unique_ptr管理的对象,但的unique_ptr对象从删除对象的责任释放。别人(你)必须手动删除该对象。 下面的下面的代码会导致内存泄漏,因为飞机对象仍然活着在main()退出。
The following code below causes a memory leak because the Aircraft object is still alive at large once the main() exits.
int main() { unique_ptr<aircraft> myAircraft = make_unique<aircraft>("F-22"); Aircraft* rawPtr = myAircraft.release(); return 0; } </aircraft></aircraft>Recommendation: Anytime you call Release() on an unique_ptr, remember to delete the raw pointer. If your intent is to delete the object managed by the unique_ptr, consider using unique_ptr.reset().
建议:任何时候你调用上的unique_ptrRelease(),记得删除原始指针。如果你的目的是要删除的管理的unique_ptr对象,可以考虑使用unique_ptr.reset()
曹纪乾曹浩洋的专栏 个人主页|我的博客 http://blog.csdn.net/sergeycao/article/details/52538123 曹纪乾曹浩洋的专栏个人主页|我的博客
Before you can use a weak_ptr, you need to acquire the weak_ptr by calling a lock() method on the weak_ptr. The lock() method essentially upgrades the weak_ptr to a shared_ptr such that you can use it. However, if the shared_ptr object that the weak_ptr points to is no longer valid, the weak_ptr is emptied. Calling any method on an expired weak_ptr will cause an ACESS VIOLATION.
之前,你可以使用weak_ptr的,你需要通过调用weak_ptr的锁()方法来获得的weak_ptr。lock()方法主要升级的weak_ptr到一个shared_ptr,这样你可以使用它。但是,如果该weak_ptr的指向不再有效,则weak_ptr的是空shared_ptr的对象。呼吁过期的weak_ptr任何方法将导致发生访问冲突。 例如,在下面的代码片段,即“mywingMan”的weak_ptr指向shared_ptr的到已经通过pIceman.reset()销毁。如果我们通过myWingman的weak_ptr现在执行任何操作,它会导致访问冲突。
For example, in the code snippet below, the shared_ptr that “mywingMan” weak_ptr is pointing to has been destroyed via pIceman.reset(). If we execute any action now via myWingman weak_ptr, it’ll cause an access violation.
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; } </aircraft></aircraft></aircraft></aircraft>It can be fixed easily by incorporating the following if check before using the myWingMan weak_ptr.
它可以很容易地通过将以下如果使用的是myWingMan的weak_ptr前检查是固定的。
if (!pMaverick->myWingMan.expired()) { cout << pMaverick->myWingMan.lock()->m_flyCount << endl; }EDIT: As many of my readers pointed out, the above code should not be used in a multithreaded environment – which equates to 99% of the software written nowadays. The weak_ptr might expire between the time it is checked for expiration and when the lock is acquired on it. A HUGE THANKS to my readers who called it out ! I'll adopt Manuel Freiholz's solution here : Check if the shared_ptr is not empty after calling lock() and before using it.
编辑:由于我的许多读者指出,上面的代码不应该在多线程环境中使用 - 这相当于时下编写的软件的99%。该weak_ptr的可能将检查其是否过期,当在其上获取锁定的时间之间到期。一个非常非常感谢我的读者谁叫出来!我在这里采用曼努埃尔Freiholz的解决方案:检查的shared_ptr不调用lock()后使用它之前,空的。
shared_ptr<aircraft> wingMan = pMaverick->myWingMan.lock(); if (wingMan) { cout << wingMan->m_flyCount << endl; }Recommendation: Always check if a weak_ptr is valid – actually if a non-empty shared pointer is returned via lock() function before using it in your code.
建议:经常检查是否有weak_ptr的是有效的 - 实际上,如果一个非空的共享指针在代码中使用它之前通过lock()函数返回。
曹纪乾曹浩洋的专栏 个人主页|我的博客