While debugging crash in a multithreaded application I finally located the problem in this statement:
CSingleLock(&m_criticalSection, TRUE);
Edit: As j_random_hacker notes, it is possible to force the user to declare a named object in order to take out a lock.
However, even if creation of temporaries was somehow banned for your class, then the user could make a similar mistake:
// take out a lock:
if (m_multiThreaded)
{
CSingleLock c(&m_criticalSection, TRUE);
}
// do other stuff, assuming lock is held
Ultimately, the user has to understand the impact of a line of code that they write. In this case, they have to know that they're creating an object and they have to know how long it lasts.
Another likely mistake:
CSingleLock *c = new CSingleLock(&m_criticalSection, TRUE);
// do other stuff, don't call delete on c...
Which would lead you to ask "Is there any way I can stop the user of my class from allocating it on the heap"? To which the answer would be the same.
In C++0x there will be another way to do all this, by using lambdas. Define a function:
template <class TLock, class TLockedOperation>
void WithLock(TLock *lock, const TLockedOperation &op)
{
CSingleLock c(lock, TRUE);
op();
}
That function captures the correct usage of CSingleLock. Now let users do this:
WithLock(&m_criticalSection,
[&] {
// do stuff, lock is held in this context.
});
This is much harder for the user to screw up. The syntax looks weird at first, but [&] followed by a code block means "Define a function that takes no args, and if I refer to anything by name and it is the name of something outside (e.g. a local variable in the containing function) let me access it by non-const reference, so I can modify it.)
What about the following? Slightly abuses the preprocessor, but it's clever enough that I think it should be included:
class CSingleLock
{
...
};
#define CSingleLock class CSingleLock
Now forgetting to name the temporary results in an error, because while the following is valid C++:
class CSingleLock lock(&m_criticalSection, true); // Compiles just fine!
The same code, but omitting the name, is not:
class CSingleLock(&m_criticalSection, true); // <-- ERROR!