这对C ++中的“mutable”有用吗?

时间:2011-09-28 06:18:59

标签: visual-c++ static-libraries

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

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

我想出了一个设计选择:

因为我经常会查询文件名,以便最小化堆分配,如果我返回std::wstring(我反复看到这是我的程序中的瓶颈)会发生,我决定将name字段保留在对象本身内,然后返回指向它的指针,如图所示。

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

当然,(query system for name)部分的部分如图所示,因为name不是mutable

一方面,调用者不会期望更改文件名。但另一方面,无论如何, const意味着什么。名称肯定会改变,只是调用者无法修改它。所以它看起来不应该是一个问题,但我不太确定。

在什么情况下,我在这里使用mutable是个好主意?为什么呢?

注1:Windows 保证文件名长度最多为256个字符,因此这里没有要考虑的缓冲区溢出问题。 < / p>

注意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引用更改

3 个答案:

答案 0 :(得分:2)

正如我所看到的,这里缺少的方法是setName。你的代码没有问题,因为没有办法改变name,而通过getName进行更改是很尴尬的。所以,假设你有一些

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

现在问题是您希望如何使用它。有意义的是,谁应该更改名称,可以访问非const 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更改const FileHandle的名称。对我来说,这看起来像是一个糟糕设计的标志,你实际上是在攻击你自己的代码。就此而言,您可以const_cast FileHandle,并在不使用mutable的情况下进行更改。但我真的不知道你的具体情况,所以也许它确实有意义......


根据getName实际检查文件名称的信息进行更新,并在返回之前根据需要更新name:在这种情况下,使name可变实际上是方式去,因为否则它不能从const方法中改变。通常不建议getter更改其值正在获得的成员的值,但如果您的情况决定了这一点,那么将name设为mutable将是有意义的。

答案 1 :(得分:0)

  

当然,文件的名称可能随时间而变化

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

<强>更新

如果预期该属性会从程序声明外部更改,那么您应该通过声明函数const volatile来创建该语句,然后使用mutable 可以是有道理的。但请注意,这种方法存在另一个问题,即函数的调用者将指针保持在文件名周围,随后对函数的调用将改变其内容。这意味着此功能的结果也应该被视为volatile

更新2:

标准中没有规定使用const的规则,没有人会阻止您标记所有功能const和您的成员volatile。但是,const通常用于表示通过调用成员函数不会更改对象的逻辑const; volatile通常用于表示可以从应用程序外部更改值。问题是关于mutable良好使用 - 我认为是主观的 - 并且mutable不是这个特定用例的局外人,特别是如果函数有{{1}修饰符也是如此。但是,volatile修饰符很少见,const volatile函数在后续调用很少后返回不同的值,并且在控件之外更改的值很少。不仅要考虑功能签名,还要考虑文档应该包含的警告数量,我认为这个意外因素至少在我的书中被认为是一个不好的用例。

答案 2 :(得分:0)

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

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

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