In my current position I spend a lot of time battling against a fairly poorly-written C++ code base. The code, while technically written in C++, is actually more of a C-like “splat” with a few classes thrown in. Since I began working on this project I’ve seen many cases where proper object-orientation would have made a drastic improvement to the quality of the code.
And it’s these cases which are the inspiration for this blog post.
Objects are not only good for encapsulation and making more readable/maintainable code. They can also be used to improve code safety. Rather than try and explain through words what I mean by ‘code safety’, let’s work through a couple of examples.
Example 1: Data Integrity in a Multithreaded Application
Let’s say that we’re working on a multithreaded application. We have a set of data that is constantly read and written across the different threads. We obviously don’t want to have different threads writing to the dataset at the same time, as this may cause all kinds of issues with the data’s integrity. We want to put a mechanism in place to ensure that the data’s integrity is never put at risk. We can do this simply by ‘locking’ the sections of code that perform the data writes via a synchronisation technique such as a critical section, a semaphore or a mutex. For the sake of this example, we’ll use a critical section. We’ll wrap it up in an object for easy use. Let’s pretend that we have a C++ critical section class that looks like this:[source:cpp]class CriticalSection
{
public:
CriticalSection();
~CriticalSection();
bool Lock();
bool Unlock();
// …
};[/source]
To use the critical section object, all we need to do is this:[source:cpp]CriticalSection importantDataLocker;
// …
if( importantDataLocker.Lock() )
{
// do stuff to important data
// …
importantDataLocker.Unlock();
}[/source]
Easy enough isn’t it! We’ll use this code in our data writer object, so that we can make sure that all of our threads are playing nicely while writing the data. During the course of our data writing, we might want to do some other processing that is not related to writing data. Even though this task isn’t related to writing the data out, we still retain the lock so that we can finish off the job after without having to unlock and relock. So we may have some code that looks like this:
[source:cpp]bool DataWriter::WriteFunkyData( FunkyData* data )
{
if( m_dataLocker.Lock() )
{
WriteABitOfData( data );
DoSomeProcessing( data );
WriteSomeMoreData( data );
// …
m_dataLocker.Unlock();
return true;
}
return false;
}[/source]
Not really rocket science is it? The above code makes sure that all the code between Lock() and Unlock() is only run when no other threads are requesting a lock (or own a lock) on the critical section.
Unfortunately we have a problem. In a non-perfect world (like the one we live in) there’s always a chance that somewhere between the call to Lock() and the call to Unlock() an error might occur. An exception may be thrown. The code might just return;. If that’s the case, the critical section will not be unlocked because the Unlock() function won’t be called before the function finishes.
The result? A critical section that’s permanently locked, and all future Lock() requests will either hang or fail. This is not ideal. If we were using system-wide mutexes we’d do some serious damage! Cleary this isn’t acceptable.
Most procedural programmers will adjust the code in the following manner:
[source:cpp]bool DataWriter::WriteFunkyData( FunkyData* data )
{
if( m_dataLocker.Lock() )
{
if( !WriteABitOfData( data ) )
{
m_dataLocker.Unlock();
return false;
}
try
{
DoSomeProcessing( data );
}
catch(…)
{
m_dataLocker.Unlock();
return false;
}
if( !WriteSomeMoreData( data ) )
{
m_dataLocker.Unlock();
return false;
}
// …
m_dataLocker.Unlock();
return true;
}
return false;
}[/source]
Some of you might be laughing at the above code. And rightly so! But don’t think that it’s uncommon because it’s not. As you can imagine, the code gets bigger, nastier and tougher to maintain. As the function increases in size, code duplication increases and the chance of making mistakes increases. What if a new maintenance coder decides to early-out without unlocking? You’re still stuck with the same problem.
Further use of object-orientation is what’s required to solve this problem. Let’s use and abuse a couple of the core features of objects: construction and destruction:
- When an object is instantiated, it’s constructor is called.
- When an object is destroyed, it’s destructor is called
For those of you who don’t know, objects are destroyed in two ways:
- When they go out of scope.
- When they are deleted.
Each function in C++ has it’s own scope. As soon as the function is finished, the scope is no longer valid and any local variables are cleaned up. This happens when …
- … program execution reaches the end of the function.
- … a return statement is reached.
- … a exception is thrown that is not caught.
If there was an object that was able to lock the critical section when it’s constructed and unlock the same critical section when it’s destroyed, we could use it to make sure that the critical section is unlocked regardless of the way in which the function is exited. That was a bit of a mouthful, so to demonstrate the point have a look at the following code:[source:cpp]class CriticalSectionLocker
{
public:
CriticalSectionLocker( CriticalSection& cs )
: m_criticalSection( cs )
{
m_isLocked = m_criticalSection.Lock();
}
~CriticalSectionLocker()
{
if( m_isLocked )
{
m_criticalSection.Unlock();
}
}
bool IsLocked()
{
return m_isLocked;
}
private:
bool m_isLocked;
CriticalSection& m_criticalSection;
};[/source]
If we use this object instead of the verbose and repetitive method shown above, our code would look like this:
[source:cpp]bool DataWriter::WriteFunkyData( FunkyData* data )
{
CriticalSectionLocker csl( m_dataLocker );
if( csl.IsLocked() )
{
if( !WriteABitOfData( data ) )
{
return false;
}
DoSomeProcessing( data );
if( !WriteSomeMoreData( data ) )
{
return false;
}
// …
return true;
}
return false;
}[/source]
This code is substantially easier to maintain. As soon as csl goes out of scope, the critical section will be unlocked automatically via the destructor. This will happen on early-outs and when exceptions are thrown. Job done!
Example 2: Flag/Value Toggling
Developers have been known to write code which relies on object state to function differently. While this might not be considered ‘best practice’, it’s fairly common out in the wild. There are times where a developer may want to temporarily toggle a flag, or change the value of a variable, and change it back before the rest of the code is run. Here’s an example (it’s not real-life, but it should give you the idea):[source:cpp]void Person::GotToParty( NightClub* nc )
{
if( !nc->AllowsEntry( this ) )
{
// temporarily pretend we’re older
// so we can go clubbing!
int backupAge = m_Age;
m_Age = 25;
if( nc->AllowsEntry( this ) )
{
this->Party( nc, HARD_BUT_SUBTLE );
}
// best restore our age now
m_Age = backupAge;
}
else
{
this->Party( nc, HARD );
}
}[/source]
Don’t laugh, we’ve all been there!
It should be obvious that the code above suffers from the same shortcomings as the previous example. It’s not resiliant against early-outs or exceptions. A similar type of construct/destruct mechanism can be used to reset the original value of a variable in a much safer way. An example class might look like this:[source:cpp]template< typename T >
class ValueToggler
{
public:
ValueToggler( T& variableToToggle, T tempValue )
: m_toggleVar( variableToToggle ),
m_originalValue( variableToToggle )
{
m_toggleVar = tempValue;
}
~ValueToggler()
{
m_togglerVar = m_originalValue;
}
private:
T& m_toggleVar;
T m_originalValue;
};[/source]
The object above is templated so that it can be used with a variety of types (eg. int, double, float). Using an object such as this, the example program code would change to the following:[source:cpp]void Person::GotToParty( NightClub* nc )
{
if( !nc->AllowsEntry( this ) )
{
ValueToggler
if( nc->AllowsEntry( this ) )
{
this->Party( nc, HARD_BUT_SUBTLE );
}
}
else
{
this->Party( nc, HARD );
}
}[/source]
Again, the code is smaller and a lot more bulletproof. The value of m_Age will be restored no matter what happens inside the function.
Conclusion and Final Thoughts
The above samples should show you how easy it is to use objects to help you write more defensive code. Though some people (usually the procedural programmers
) would say that this kind of code is a hack, it’s my view that they’re wrong. The code is clear, easy to maintain, and very safe.
Disclaimer: The code above hasn’t been compiled, so it may contain errors. The point of the post is to show you the idea behind using objects for safety. Ideal implementations are beyond the scope of the article.
Thoughts and feedback are always welcome.










August 9, 2007
Excellent Post! This is why I like OO programming, the constructors! - Passing the variables once on the creation of an instance and then calling quite a few functions (without having to pass all the variables again) is nice.
I will have to be honest and say i hardly ever use the Deconstructor, mainly because i forget its there. Ill definatly be using it in the future
August 9, 2007
Cheers Matt.
I’m going to sound picky, but they’re called destructors not deconstructors
Generally they should be used for cleanup code mate. Any resources that you might still have allocated should be released when your object is destroyed. It’s an ideal spot to put clean up code as it’s always called when your object dies, which means it’s done automatically rather than manually. Very handy indeed!
August 9, 2007
Lol, oops :P
Yeah, I probably would have done any clean up code outside of the object or in a function when I should have been using a destructor
Like I say. Uni is crap lol
August 13, 2007
I have this problem with my game and I solved it the exact same way - setting a locked flag so if the deconstructor is called it will unlock resources on it’s way out.
My main loop is called in a separate thread from the Windows message buffer and the screen refresh thread so it’s common for some glitch to try to throw an exception out of the inner thread and leave the resources hanging.
I don’t want to complicate your elegant solution above but is it true Vista will keep a list of all threads running within an app and allow them to be closed by default on exit? This doesn’t quite work correctly every time on either Win98, XP or 2000.
August 13, 2007
I just said deconstructor to annoy you.
August 13, 2007
To be honest, I’m not sure mate. I’d have to do some research before being able to answer that. I’ll have a read and see if I can find anything.
August 23, 2007
Vault-Co,
I’ve had a brief look around, and I can’t seem to find any decent information on the implementation of threading in Vista. I’d be surprised if it was too far different from XP to be honest, but that’s just me
Either way, if it does work nicely on Vista, you’ve still got to worry about the earlier versions (particularly XP which will be around for a lot longer than MS thinks).
December 8, 2007
Ace post. You worked with me somewhere this kind of stuff was banned in the coding standards which was bloody annoying. It’s sometimes hard to get back into good habits!
As for the thread thing, I thought an OS kills all of a processes threads when a process exits anyway (if you haven’t already done so yourself). If Windows doesn’t do this, that’s really really bad for an OS. Am I misinterpreting Vault-Co’s comment?
December 8, 2007
Yeah, I was pretty disturbed when I found things like this were banned. What was more disturbing was the justification behind it:
Oh right! Idiot. Let’s hope that over time they’ll realise that constructors are the same as any other practice or feature in a language: perfectly acceptable if used properly.
I’m not 100% sure of the answer to the question. Background threads should be terminated when the main/foreground thread is closed. This is at least the case in .NET. If CreateThread() and _beginthreadex() both essentially create a background thread (or “green” threads) then surely the OS would clean up the thread when the process exits.
Unfortunately I don’t know for sure. All I know is that I make sure I clean up all my threads manually before my application exits, as I’d rather make sure that things have been tidied up instead of hoping that something else is going to do it for me.
December 10, 2007
Sure, I’m definitely in the clean up after yourself camp, though some people are saying with Windows you should just kill your process after making sure the user’s data is saved, otherwise you end up with programs that take > 30 seconds to shut down (Visual Studio for example). Anything built in .NET seems fairly heavyweight in this regard.
December 11, 2007
Yes this is true. But I’d take a slower shut-down time with the bonus of having the system up for longer, rather than relying on the OS to clean up and possibly leave resources open. Inevitably, you’ll need to reboot to get everything back. Uptime is high on the priority list. For me, it’s a win if it’s takes longer to close so I don’t have to reboot!