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:
- a Factory of sorts
- Some Object created by that factory
The factory definition looks something like this:
#pragma once
#include
#include "MyObject.h"
class MyFactory
{
public:
std::shared_ptr 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
class MyFactory;
class MyObject
{
public:
MyObject(std::shared_ptr 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 MyFactory::GetObject()
{
return make_shared(shared_ptr(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.
Happy coding…