Reputation: 91
I am maintaining a library that contains a Mutex class. I cannot decide if the lock() and unlock() functions exposed by this class should be const or not. I looked for similar code on the Web, and could find both implementations.
First implementation, lock() and unlock() are not const. It means someone who uses the Mutex class in a const function should put extra effort to call Mutex's functions:
class Mutex
{
public:
void lock();
void unlock();
};
class Foo
{
public:
void getBar(Bar& bar) const
{
m_mutex.lock();
bar = m_bar;
m_mutex.unlock();
}
private:
// Mutable, responsability of the user (or we could use a const_cast when needed)
mutable Mutex m_mutex;
Bar m_bar;
};
Second implementation, lock() and unlock() are const, even if that does not sound very natural (as we modify the Mutex instance), but the user does not have to bother when calling those functions in one of his const functions:
class Mutex
{
public:
void lock() const;
void unlock() const;
};
class Foo
{
public:
void getBar(Bar& bar) const
{
m_mutex.lock();
bar = m_bar;
m_mutex.unlock();
}
private:
Mutex m_mutex;
Bar m_bar;
};
What solution do you prefer ? I hope your opinions will help me to take a decision.
Upvotes: 9
Views: 3750
Reputation: 27375
The first implementation is preferred: the const condition refers to logical const-ness vs. binary const-ness: the mutex is a helper class - it does not actually change the logical state of the client code, but changing it does change the binary state of the client code.
As such, the mutex should be made mutable
in the client code.
The same situation arises if you are caching some costly operation results in your code: you declare the chache variable as mutable, so when computing it you don't need to throw away the const
-ness of the class.
Upvotes: 3
Reputation: 504073
mutable
was made for this kind of stuff. Namely, mutable
applies to things that don't take part in the logical constness of an object. (If one value is "logically constant", it means the 'primary' values are constant; the value of the object, from the outside, cannot change.)
The value of your object is independent from the state of the mutex (it's only an implementation detail to provide consistency, and it's state is not known outside the class), which is a good sign your mutex should be mutable
.
Note, you should not go the const_cast
route. This leads to undefined behavior if you did:
const Foo f;
Bar b;
f.getBar(b); // takes const off of mutex, modifies (bang you're dead)
Upvotes: 10
Reputation: 73473
I prefer the first option of making the Mutex
mutable
in the client code. By this, the user of the class explictly knows that he is calling a non-const function from his const function. In fact if you see the MFC implementation of CMutex class you can see Lock
and Unlock
methods are non-const.
Upvotes: 8
Reputation: 1141
If you want to derive classes from the Mutex class, then it makes (some) sense to make the two functions const, then you can call it in the derived class's own const functions.
Like this:
class Foo : public Mutex {
public:
void
bar() const { lock(); doSomething(); unlock(); }
}
Upvotes: 0