开发者

Is this a good use for "mutable" in C++?

I have a class that wraps a file handle:

class FileHandle
{
    HANDLE hFile;
    TCHAR name[256];
public:
    LPCTSTR getName() const { /*(query system for name)*/ return this->name; }
};

I have come up with a design choice:

Since I will be querying the file name often, in开发者_开发技巧 order to minimize heap allocations which would happen if I returned std::wstring (I have repeatedly seen that this is a bottleneck in my programs), I have instead decided to keep a name field inside the object itself, and just return a pointer to it as shown.

Of course, the name of the file can change over time, so I can't avoid querying it every time. I can only avoid the reallocation.

Of course, the section saying (query system for name) would not work as shown because name isn't mutable.

On the one hand, the caller doesn't expect the file name to change. But on the other hand, that's not what const means, anyway. The name can certainly change, it's just that the caller can't modify it. So it doesn't look like it should be a problem, but I'm not too sure.

Under what circumstances would it be a good idea for me to use mutable here? Why?

Note 1: Windows guarantees that file names are at most 256 characters long, so there's no buffer overflow issue to consider here.

Note 2: The class is only designed for single-threaded use. I'm not worried about concurrent modifications, only modifications in between statements.


Why const doesn't imply immutability:

This should be self-explanatory:

FileHandle file = ...;
const FileHandle &fileConst(file);
LPCTSTR name1 = fileConst.getName();
file.setName(_T("new name"));
LPCTSTR name2 = fileConst.getName();

Now name1 and name2 aren't equal. So not only can the file name change quite easily, but name1 itself can also change -- even though they're both const. There's no rule saying that const members can't change, just that they can't be changed through the const reference.


The missing method here, as I see it, is setName. There's no problem with your code as it is, because there's no way to change name, and changing it via getName is awkward. So, assuming you'd have some

void setName(LPCTSTR newName) { _tcscpy(name, newName); /*or so*/ }

Question now is how you'd expect it to be used. It makes sense that whoever is supposed to be changing the name, has access to a non-const FileHandle. In such case, there's no problem using this trivial setName. If, however, the file name should be changed on a const FileHandle, you have two problems: first, it's awkward... and second, you won't be able to call the setName above. To be able to call it, you'd have to change it to

void setName(LPCTSTR newName) const { _tcscpy(name, newName); /*or so*/ }

which doesn't really makes sense either, but let's pretend it does. Now, this won't work, because FileHandle being const would effectively make name const as well. And this finally brings us to mutable: changing name's declaration to:

mutable TCHAR name[256 + 1 /*for NULL terminator*/];

will indeed allow you to use setName to change the name of a const FileHandle. To me, this looks like a sign of a bad design, where you're actually hacking your own code. For that matter, you can just const_cast the FileHandle, and change it without using mutable. But I really don't know the specifics of your case, so maybe it does make sense...


Update, given the information that getName actually checks the name of the file, and updates name if needed before returning it: in that case, making name mutable would indeed be the way to go, because otherwise it can't be changed from within a const method. It is generally not advised for a getter to change the value of the member whose value it's getting, but if your case dictates that, then making name a mutable would make sense.


I would say it is acceptable, but not ideal. I think logically you have a name that is independent the file handle. You are not asking for the name of the file handle, you are asking for the name of the file that the file handle is referring to. Getting that name doesn't change the file handle, so the logical constness is preserved. What is not ideal is that the results could change in unexpected ways:

LPCTSTR old_name = filehandle.getname();
changeFileName(filehandle);
LPCTSTR new_name = filehandle.getname();
// but old_name and new name still match!

But that is really independent of whether or not the member should be made mutable.


Of course, the name of the file can change over time

Are you sure about that? Can a file name be changed while there is an open handle to it? If that's the case, then this is not a good use of mutable. mutable is there to achieve logical constness when bitwise constness cannot be achieved, like is the case with cached data. However, callers of your getName() function would be surprissed if two consecutive const calls would return different values.

Update:

If the property is expected to change from outside the program declaration, then you should make that statement by declaring the function const volatile, and then the use of mutable may be justified. However note there is another problem with this approach, in that a caller of the function keeps the pointer to the file name around and a subsequent call to the function will change its contents. That means that the result of this function should be considered volatile as well.

Update 2:

There is no rule in the standard that dictates the uses of const, no one will stop you from flagging all your functions const and your members volatile. However, const is usually used to indicate that the logical constness of the object won't change by invoking a member function; and volatile is usually used to indicate that the value may be changed from outside the application. The question is about good use of mutable -which I consider subjective-, and mutable is not an outsider to this particular use case specially if the function has a volatile modifier as well. However, const volatile modifiers are rare, const functions returning different values after subsequent calls are rare, and values being changed outside your control are rare. Considering not only the function signature but the ammount of warnings that the documentation should include, I think that the surprise factor is high enough for this to be considered a bad use case at least in my book.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜