.NET Zone is brought to you in partnership with:

Dror Helper is an experienced software developer has written and designed software in various fields including video streaming, eCommerce, performance optimization and unit testing tools. He is passionate about programming best practices and all things software development, and has been a guest presenter at several user group meetings and ALT.NET events. Dror's blog can be found at http://blog.drorhelper.com where he writes about unit testing, agile methodologies, development tools, programming languages and anything else he finds interesting. Dror is a DZone MVB and is not an employee of DZone and has posted 57 posts at DZone. You can read more from them at their website. View Full User Profile

C++ Bug hunt - shared_ptr Misuse

10.04.2013
| 1909 views |
  • submit to reddit
C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off.
from Bjarne Stroustrup's FAQ: Did you really say that?

I want to share with you a strange bug I’ve been struggling with last week.
I had an application that crashed whenever a user logged out and then logged back. I couldn’t understand why during the 2nd login attempt all hell broke loose – I got the finger equivalent of the C++ world - “corrupted memory” exception.
I had the best minds try and use their debugging powers to try and catch the cause of that memory issue thing but to no avail. That is until a simple breakpoint solved the mystery.

The cast

Due to NDA and time constraint let me simplify the problem- I had two classes:

  1. a Factory of sorts
  2. Some Object created by that factory

The factory definition looks something like this:

#pragma once
 
#include <memory>
#include "MyObject.h"
 
class MyFactory
{
public:
    std::shared_ptr<myobject> GetObject();
 
    //...
};

The “Object” class had many important methods that did interesting stuff to the benefit of the users – however for this example we only care about its constructor:

#pragma once
#include <memory>
 
class MyFactory;
 
class MyObject
{
public:
    MyObject(std::shared_ptr<myfactory> factory);
    ~MyObject(void);
 
    //...
};

And that’s it – see the problem yet?

Obviously this cyclic reference (factory is passed to object that was created by factory)  is asking for trouble  but that’s not the problem (yet).

The problem

Let me show you how GetObject was implemented:

using namespace std;
 
std::shared_ptr<myobject> MyFactory::GetObject()
{
    return make_shared<MyObject>(shared_ptr<myfactory>(this));
}

Do you see the problem? If not - take a minute to think about it…

In case you’re not familiar with shared_ptr – it’s essentially a smart pointerwhich was introduced in C++ 11 (and before that in Boost):

Manages the storage of a pointer, providing a limited garbage-collection facility, possibly sharing that management with other objects.
from cplusplus.com

The problem is that a misguided soul (read: yours truly) has used it the wrong way.

shared_ptr works by reference counting – each new assignment increase that count and every time the object goes out of scope the count is decreased.

  • When I’ve created a new shared_ptr instance from this it was created with count of one.
  • All was good and well until after MyObject constructor ended in which point the count was decreased.
  • Since  no other shared_ptr objects were holding the pointer – MyFactory desructor was called
  • An exception – not right away (we’re talking C++ here) but most of the time the next time you try and do something with MyFactory

The fix was simple – I’ve changed the c’tor to receive MyFactory as a plain-old-honest-to-god reference.

The lesson learnt

shared_ptr is a great thing – it’s one the useful new gadgets that made it into the new C++ 11.

I’m using it all the time – it help me write simple, elegant C++ code, But like everything else in the C++ world – make sure you understand what you’re doing.

Published at DZone with permission of Dror Helper, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)