这是“可变”的好用途吗?在 C++ 中?

发布于 2024-12-07 11:20:00 字数 1333 浏览 0 评论 0原文

我有一个包装文件句柄的类:

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

我提出了一个设计选择:

因为我将经常查询文件名,以便最大限度地减少如果我返回 std::wstringstd::wstring 时会发生的堆分配code> (我多次发现这是我的程序中的瓶颈),我决定在对象本身内部保留一个 name 字段,并只返回一个指针如图所示。

当然,文件的名称可能会随着时间的推移而改变,所以我无法避免每次都查询它。我只能避免重新分配

当然,(名称查询系统) 部分将无法如图所示工作,因为 name 不是可变的

一方面,调用者不希望文件名发生更改。但另一方面,无论如何,这不是 const 的意思。名称当然可以改变,只是调用者无法修改它。所以看起来这不应该是一个问题,但我不太确定。

在什么情况下我最好在这里使用 mutable ?为什么?

注意 1:Windows保证文件名长度最多为 256 个字符,因此这里无需考虑缓冲区溢出问题。

<强>注2:该类仅设计用于单线程使用。我不担心并发修改,只担心之间的修改 陈述。


为什么const 并不意味着不变性:

这应该是不言自明的:

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

现在 name1name2 不相等。因此,不仅文件名可以很容易地更改,name1本身也可以更改 - 即使它们都是const。没有规则说 const 成员不能更改,只是不能通过 const 引用更改它们。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

靑春怀旧 2024-12-14 11:20:00

据我所知,这里缺少的方法是 setName。您的代码本身没有问题,因为无法更改 name,并且通过 getName 更改它很尴尬。因此,假设您现在有一些

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

问题是您希望如何使用它。这是有道理的,无论谁应该更改名称,都可以访问非常量的FileHandle。在这种情况下,使用这个简单的 setName 没有问题。但是,如果应该在 const FileHandle 上更改文件名,则会遇到两个问题:首先,这很尴尬……其次,您将无法调用 setName上面。为了能够调用它,您必须将其更改为

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

这也没有真正意义,但让我们假设它确实有意义。现在,这不起作用,因为 FileHandle 为 const 也会有效地使 name 也为 const。这最终给我们带来了 mutable:将 name 的声明更改为:

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

确实允许您使用 setName 来更改 name 的名称代码>常量文件句柄。对我来说,这看起来像是糟糕设计的标志,实际上是在破解自己的代码。就此而言,您只需 const_cast FileHandle 即可,并在不使用 mutable 的情况下更改它。但我真的不知道你的情况的具体情况,所以也许它确实有意义......


更新,考虑到 getName 实际检查文件名并更新 name 的信息 如果在返回之前需要的话:在这种情况下,使 name 可变确实是一种可行的方法,因为否则它不能在 const 方法中更改。通常不建议 getter 更改其所获取值的成员的值,但如果您的情况需要这样做,那么将 name 设为 mutable 是有意义的。

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.

弥繁 2024-12-14 11:20:00

我想说这是可以接受的,但并不理想。我认为逻辑上你有一个独立于文件句柄的名称。您不是在询问文件句柄的名称,而是在询问文件句柄所引用的文件的名称。获取该名称不会更改文件句柄,因此保留了逻辑常量。不理想的是结果可能会以意想不到的方式发生变化:

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

但这实际上与成员是否应该可变无关。

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.

家住魔仙堡 2024-12-14 11:20:00

当然,文件的名称可以随着时间的推移而改变

您确定吗?当文件句柄处于打开状态时,可以更改文件名吗?如果是这样的话,那么这不是 mutable 的好用。当无法实现按位常量时,mutable可以实现逻辑常量,就像缓存数据的情况一样。但是,如果两个连续的 const 调用返回不同的值,getName() 函数的调用者将会感到惊讶。

更新:

如果预计属性会从程序声明外部更改,则应通过声明函数 const volatile 来做出该声明,然后使用 可变 可能是合理的。但请注意,这种方法还有另一个问题,即函数的调用者保留指向文件名的指针,并且对该函数的后续调用将更改其内容。这意味着该函数的结果也应被视为易失性

更新 2:

标准中没有规定 const 的使用规则,没有人会阻止您标记所有函数 const 和您的成员不稳定。然而,const通常用于表示对象的逻辑常量不会因调用成员函数而改变;和易失性通常用于指示该值可以从应用程序外部更改。问题是关于 mutable良好使用 - 我认为这是主观的 - 并且 mutable 并不是这个特定用例的局外人,特别是如果函数也有一个 volatile 修饰符。但是,const volatile 修饰符很少见,const 函数在后续调用后返回不同值的情况很少见,并且在您控制之外更改值的情况也很少见。不仅考虑到函数签名,还考虑到文档应包含的警告数量,我认为令人惊讶的因素足够高,至少在我的书中,这被认为是一个糟糕的用例。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文