C++ 在这种情况下可变合适吗?
我想问一下这里使用mutable是否合适:
#include <iostream>
class Base
{
protected:
int x;
public:
virtual void NoMod() const
{
std::cout << x << std::endl;
}
void Draw() const
{
this->NoMod();
}
};
class Derive : public Base
{
private:
mutable int y;
public:
void NoMod() const
{
y = 5;
}
};
int main()
{
Derive derive;
// Test virtual with derive
derive.Draw();
return 0;
}
Base类是一个3rd-party库。 我正在扩展它以提供我自己的 NoMod()。 库原始 NoMod() 被声明为 const。
我的NoMod()与Base的不同之处在于它需要修改自己的成员变量。
因此,为了在调用 Draw() 时编译并调用我自己的 NoMod(),我必须
1) 将 Derive::NoMod() 实现为 const
2)使我的 int y 可变。
这是我能做的最好的事情吗?
I would like to ask if the use of mutable is appropriate here:
#include <iostream>
class Base
{
protected:
int x;
public:
virtual void NoMod() const
{
std::cout << x << std::endl;
}
void Draw() const
{
this->NoMod();
}
};
class Derive : public Base
{
private:
mutable int y;
public:
void NoMod() const
{
y = 5;
}
};
int main()
{
Derive derive;
// Test virtual with derive
derive.Draw();
return 0;
}
The Base class is a 3rd-party library. I'm extending it to provide my own NoMod(). The library original NoMod() is declared as a const.
My NoMod() differs from Base in the fact that it needs to modify its own member variable.
Thus, for my own NoMod() to compile and get called when Draw() is called, I had to
1) Implement Derive::NoMod() as a const
2) make my int y mutable.
Is this the best I can do?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
正如“head geek”所描述的,您问题的答案取决于您的数据成员的使用方式。
我区分类中的两种类型的数据成员。
我使用通用术语“属性”来指代数据成员
这是对象的逻辑状态或“值”。
通常,属性很少被声明为可变的。
我创造了原生论“贡献”,它表示
只是“工作内存/存储”的数据成员
并且这在某种程度上脱离了对象的状态。
贡献与对象的用户没有上下文相关性,
它们存在于类中只是为了维护
以及对象的高效运行。
贡献通常在类中声明为可变的并且始终
私有或受保护。
例如,假设您的对象是一个链接列表,
所以你有一个指向列表中第一项的指针。
我认为这个指针是一个贡献,因为
它不代表列表中的数据。
即使列表已排序并且
指针设置为列表中新的第一项,
列表对象的用户可以不关心如何
清单已维护。 只是列表数据有
是否被修改以及列表是否已排序
与用户的观点相关。
即使您有一个布尔数据成员“排序”以快速确定
如果列表处于排序状态,那也将是一个贡献
因为列表结构本身赋予了排序状态,
“排序”变量成员只是为了有效地记住状态
无需扫描列表。
再举一个例子,如果您有一个搜索列表的 const 方法。
假设您知道搜索通常会返回
最近搜索过的项目,
您将在类中保留一个指向此类项目的指针,以便您的方法
在搜索之前可以先检查最后找到的项目是否与搜索键匹配
整个列表(如果该方法确实需要搜索列表并找到
一个项目,指针将被更新)。
我认为这个指针是一个贡献,因为它
只是为了帮助加快搜索速度。 尽管
search 更新指针贡献,该方法有效
const 因为容器中的任何项目数据都没有被修改。
因此,作为属性的数据成员通常不会声明为可变的,
有助于对象功能的数据成员通常是可变的。
As 'head geek' described, the answer to your question depends on how your data member is used.
I distinguish two types of data members in a class.
I use the common term 'attribute' to refer to data members
that are the logical state or 'value' of the object.
Typically attributes are rarely declared as mutable.
I have coined the protologism 'contribute' it denote
data members that are simply 'working memory/storage'
and that are somewhat divorced from the state of the object.
Contributes have no contextual relevance to the user of the object,
they exist in the class only to contribute to the maintenance
and efficient operation of the object.
Contributes are usually declared in the class as mutable and are always
private or protected.
For example let's say your object is a linked list,
so you have a pointer to the first item in the list.
I would consider this pointer a contribute because
it does not represent the data in the list.
Even if the list is sorted and the
pointer is set to the new first item in the list,
the user of the list object could care less how the
list is maintained. Only that the list data has
been modified or not and that the the list is sorted or not is
relevant to the user's perspective.
Even if you had a booean data member 'sorted' to quickly determine
if the list is in a sorted state, that too would be a contribute
because it is the list structure itself which imbues the sorted state,
the 'sorted' variable member is used simply to efficiently remember the state
without having to scan the list.
As another example, if you have a const method that searches the list.
Suppose you know that typically the search will return the
most recently previously searched for item,
you would keep a pointer in your class to such a item so your method
can first check if the last found item matches the search key before searching
the entire list (if the method does indeed need to search the list and finds
an item, the pointer would be updated).
This pointer I would consider to be a contribute because it
is only there to help speed up the search. Even though the
search updates the pointer contribute, the method is effectively
const because none of the items' data in the container are modified.
So, data members that are attributes are usually not declared mutable,
and data members that contribute to the functioning of an object will usually be mutable.
很难说,因为您没有给出任何关于 y 指代的内容或如何使用它的上下文。
一般来说,只有当更改可变变量不会改变对象的实际“值”时,
mutable
才适用。 例如,当我为 C 样式字符串编写包装器时,我需要使内部mLength
变量可变,以便我可以缓存长度,即使它所请求的内容是 <代码>常量对象。 它没有改变长度或字符串,并且在类本身之外不可见,因此使其可变
是可以的。It's hard to say, since you don't give any context on what
y
refers to or how it's used.In general,
mutable
is only appropriate when changing the mutable variable doesn't change the actual "value" of the object. For example, when I was writing a wrapper for C-style strings, I needed to make the internalmLength
variable mutable so that I could cache the length, even if the thing it was requested on was aconst
object. It didn't change the length or the string, and wasn't visible outside of the class itself, so making itmutable
was okay.我认为
mutable
唯一可以接受的情况是对于引用计数之类的事情,这些事情实际上并不是对象状态的一部分。如果
y
是对象的物理状态的一部分,但不是逻辑状态的一部分,那么这样做是可以的,但否则,不要这样做。The only time I think
mutable
is okay is for things like reference counts that aren't really part of the object's state.If
y
is part of the object's physical state, but not logical state, then this is okay, but otherwise, don't do it.当类的成员没有真正定义对象的状态时使用可变(例如,是有助于提高性能的缓存值/对象)。
我用来做另一个区别。 在您的示例中,您仅强制对 const 对象进行一次更改。 您还可以使用 const_cast 运算符:
使用 const_cast 运算符时,您的优势在于,只需在代码中搜索运算符名称即可轻松识别强制执行 const 到非常量转换的位置。
但是,正如我所说,如果该成员不是对象状态的一部分,但必须在几个常量方法中间接更改,请对该成员使用 mutable。
Use mutable when the member of the class is not really defining the state of the object, (e.g. is a cached value/object that helps improving performance).
I use to do another difference. In your example, you only enforce change to const object only once. You can use also const_cast operator:
When you use const_cast operator, you have the advantage that you can easily identify the places where you enforced the const to non-const conversion by simply running a search in your code for the operator name.
However, as I said, if the member is not part of the state of the object, but must be changed indirectly in several constant methods, use mutable for that member.
我需要可变功能的唯一情况是:
然而,作为一般经验法则,我建议不要过于频繁地使用 mutable,因为它绕过了 C++ 出色的 const 功能。
The only situations where I needed the mutable feature are:
However, as a general rule of thumb, I'dd advise not to use mutable too often, as it bypasses C++'s wonderful const feature.
您可以想到“可变”的另一种情况是,当您有一个带有“const”成员变量的类,并且需要实现赋值运算符(=)而不跳过“const”成员的赋值时。
另外,根据 C++ 标准,const_cast 应用于最初声明的“const”变量并使用它是 UB。 因此,如果方法的接口接受必须在内部修改的“const”,请向其传递一个“可变”参数。
你可以从上面的情况来判断是否合适,即只有在语义上才有意义! 不要使用它来使源代码可编译。
问候,
Another situation where you can think of 'mutable' is when you have a class with 'const' member variables and you require to implement the assignment operator(=) without skipping the assignment of the 'const' member.
Also, const_cast apllied on originally declared 'const' variable and using it is U.B. according to C++ standard. So if the interface of a method accepts a 'const' which has to be modified internally, pass it a 'mutable' argument.
You may judge the appropriateness of the same from the above situations i.e. only of it makes sense semantically! Do not use it to make the source-code compilable.
Regards,
如果,正如您所说,它是第三方库的一部分,您可能别无选择。 C++ 本质上是一种实用语言,它可以让您做您需要做的事情,即使它可能并不总是“最佳实践”。
但需要注意的一件事是,第三方库记录了 NoMod 不应通过添加 const 说明符来修改对象。 如果违反该合同,您可能会遇到麻烦。 如果库在某些情况下多次调用 NoMod,您的派生类最好能够处理该问题,因为真正的 const 方法不会有任何问题。
我首先会寻找另一种方法来解决问题,但如果失败则声明它是可变的。
If, as you said, it's part of a third party library, you may not have a choice. C++ is at heart a pragmatic language and lets you do what you need to do, even if it may not always be a "best practice".
One thing to note though, is that the third party library is documenting that NoMod should not modify the object by adding that const specifier. By violating that contract, you leave yourself open to possible trouble. If the library in some situations call NoMod multiple times, your derived class better be able to handle that, since a true const method would have no problem with it.
I'd first look for another way to solve the problem, but failing there declare it mutable.