While debugging crash in a multithreaded application I finally located the problem in this statement:
CSingleLock(&m_criticalSection, TRUE);
First, Earwicker makes some good points -- you can't prevent every accidental misuse of this construct.
But for your specific case, this can in fact be avoided. That's because C++ does make one (strange) distinction regarding temporary objects: Free functions cannot take non-const references to temporary objects. So, in order to avoid locks that blip into and out of existence, just move the locking code out of the CSingleLock constructor and into a free function (which you can make a friend to avoid exposing internals as methods):
class CSingleLock {
friend void Lock(CSingleLock& lock) {
// Perform the actual locking here.
}
};
Unlocking is still performed in the destructor.
To use:
CSingleLock myLock(&m_criticalSection, TRUE);
Lock(myLock);
Yes, it's slightly more unwieldy to write. But now, the compiler will complain if you try:
Lock(CSingleLock(&m_criticalSection, TRUE)); // Error! Caught at compile time.
Because the non-const ref parameter of Lock() cannot bind to a temporary.
Perhaps surprisingly, class methods can operate on temporaries -- that's why Lock() needs to be a free function. If you drop the friend specifier and the function parameter in the top snippet to make Lock() a method, then the compiler will happily allow you to write:
CSingleLock(&m_criticalSection, TRUE).Lock(); // Yikes!
MS COMPILER NOTE: MSVC++ versions up to Visual Studio .NET 2003 incorrectly allowed functions to bind to non-const references in versions prior to VC++ 2005. This behaviour has been fixed in VC++ 2005 and above.