“可变”是否可以? 除了允许 const 成员函数修改数据成员之外,关键字还有其他用途吗?

发布于 2024-07-04 19:36:48 字数 369 浏览 15 评论 0原文

不久前,我遇到了一些用 mutable 关键字标记类的数据成员的代码。 据我所知,它只是允许您修改 const 限定的成员方法中的成员:

class Foo  
{  
private:  
    mutable bool done_;  
public:  
    void doSomething() const { ...; done_ = true; }  
};

这是 this 关键字的唯一用途,还是还有比表面上看起来更多的用途? 此后,我在一个类中使用了这种技术,将 boost::mutex 标记为可变的,允许 const 函数锁定它以实现线程安全原因,但说实话,感觉有点像黑客。

A while ago, I came across some code that marked a data member of a class with the mutable keyword. As far as I can see it simply allows you to modify a member in a const-qualified member method:

class Foo  
{  
private:  
    mutable bool done_;  
public:  
    void doSomething() const { ...; done_ = true; }  
};

Is this the only use of this keyword, or is there more to it than meets the eye? I have since used this technique in a class, marking a boost::mutex as mutable, allowing const functions to lock it for thread-safety reasons, but, to be honest, it feels like a bit of a hack.

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

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

发布评论

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

评论(18

饮湿 2024-07-11 19:36:50

关键字“mutable”实际上是一个保留关键字。通常它用于改变常量变量的值。如果您想要一个 constsnt 具有多个值,请使用关键字 mutable。

//Prototype 
class tag_name{
                :
                :
                mutable var_name;
                :
                :
               };   

The very keyword 'mutable' is actually a reserved keyword.often it is used to vary the value of constant variable.If you want to have multiple values of a constsnt,use the keyword mutable.

//Prototype 
class tag_name{
                :
                :
                mutable var_name;
                :
                :
               };   
燃情 2024-07-11 19:36:50

我们使用可变的最好的例子之一是在深度复制中。 在复制构造函数中,我们发送 const &obj 作为参数。 因此创建的新对象将是常量类型。 如果我们想要更改(大多数情况下我们不会更改,极少数情况下我们可能会更改)这个新创建的 const 对象中的成员,我们需要将其声明为 mutable

可变存储类只能用于类的非静态非常量数据成员。 即使类的可变数据成员是声明为 const 的对象的一部分,也可以对其进行修改。

class Test
{
public:
    Test(): x(1), y(1) {};
    mutable int x;
    int y;
};

int main()
{
    const Test object;
    object.x = 123;
    //object.y = 123;
    /* 
    * The above line if uncommented, will create compilation error.
    */   

    cout<< "X:"<< object.x << ", Y:" << object.y;
    return 0;
}

Output:-
X:123, Y:1

在上面的示例中,我们可以更改成员变量 x 的值,尽管它是声明为 const 的对象的一部分。 这是因为变量 x 被声明为可变的。 但是如果你尝试修改成员变量y的值,编译器会抛出错误。

One of the best example where we use mutable is, in deep copy. in copy constructor we send const &obj as argument. So the new object created will be of constant type. If we want to change (mostly we won't change, in rare case we may change) the members in this newly created const object we need to declare it as mutable.

mutable storage class can be used only on non static non const data member of a class. Mutable data member of a class can be modified even if it's part of an object which is declared as const.

class Test
{
public:
    Test(): x(1), y(1) {};
    mutable int x;
    int y;
};

int main()
{
    const Test object;
    object.x = 123;
    //object.y = 123;
    /* 
    * The above line if uncommented, will create compilation error.
    */   

    cout<< "X:"<< object.x << ", Y:" << object.y;
    return 0;
}

Output:-
X:123, Y:1

In the above example, we are able to change the value of member variable x though it's part of an object which is declared as const. This is because the variable x is declared as mutable. But if you try to modify the value of member variable y, compiler will throw an error.

追风人 2024-07-11 19:36:50

当您重写 const 虚函数并想要修改该函数中的子类成员变量时,可变可以很方便。 在大多数情况下,您不想更改基类的接口,因此您必须使用自己的可变成员变量。

The mutable can be handy when you are overriding a const virtual function and want to modify your child class member variable in that function. In most of the cases you would not want to alter the interface of the base class, so you have to use mutable member variable of your own.

飘落散花 2024-07-11 19:36:50

在某些情况下(例如设计不佳的迭代器),类需要保留计数或其他一些附带值,这并不会真正影响类的主要“状态”。 这是我最常看到使用可变的地方。 如果没有可变性,您将被迫牺牲设计的整体稳定性。

对我来说,大多数时候这感觉就像是黑客攻击。 在极少数情况下很有用。

In some cases (like poorly designed iterators), the class needs to keep a count or some other incidental value, that doesn't really affect the major "state" of the class. This is most often where I see mutable used. Without mutable, you'd be forced to sacrifice the entire const-ness of your design.

It feels like a hack most of the time to me as well. Useful in a very very few situations.

兔姬 2024-07-11 19:36:50

在创建用于类测试目的的存根时,可变关键字非常有用。 您可以存根 const 函数,并且仍然能够增加(可变)计数器或添加到存根中的任何测试功能。 这使得存根类的接口保持完整。

The mutable keyword is very useful when creating stubs for class test purposes. You can stub a const function and still be able to increase (mutable) counters or whatever test functionality you have added to your stub. This keeps the interface of the stubbed class intact.

春庭雪 2024-07-11 19:36:50

经典示例(如其他答案中提到的)以及迄今为止我所见过的 mutable 关键字的唯一情况是用于缓存复杂的 Get 方法的结果,其中缓存被实现为类的数据成员,而不是方法中的静态变量(出于在多个函数之间共享或简单清洁的原因)。

一般来说,使用 mutable 关键字的替代方法通常是方法中的静态变量或 const_cast 技巧。

另一个详细解释在这里

The classic example (as mentioned in other answers) and the only situation I have seen the mutable keyword used in so far, is for caching the result of a complicated Get method, where the cache is implemented as a data member of the class and not as a static variable in the method (for reasons of sharing between several functions or plain cleanliness).

In general, the alternatives to using the mutable keyword are usually a static variable in the method or the const_cast trick.

Another detailed explanation is in here.

风追烟花雨 2024-07-11 19:36:50

Mutable 将类的 const 的含义从按位 const 更改为逻辑 const。

这意味着具有可变成员的类不再是按位常量,并且将不再出现在可执行文件的只读部分中。

此外,它通过允许 const 成员函数在不使用 const_cast 的情况下更改可变成员来修改类型检查。

class Logical {
    mutable int var;

public:
    Logical(): var(0) {}
    void set(int x) const { var = x; }
};

class Bitwise {
    int var;

public:
    Bitwise(): var(0) {}
    void set(int x) const {
        const_cast<Bitwise*>(this)->var = x;
    }
};

const Logical logical; // Not put in read-only.
const Bitwise bitwise; // Likely put in read-only.

int main(void)
{
    logical.set(5); // Well defined.
    bitwise.set(5); // Undefined.
}

有关更多详细信息,请参阅其他答案,但我想强调它不仅仅是为了类型安全,而且它会影响编译结果。

Mutable changes the meaning of const from bitwise const to logical const for the class.

This means that classes with mutable members are longer be bitwise const and will no longer appear in read-only sections of the executable.

Furthermore, it modifies type-checking by allowing const member functions to change mutable members without using const_cast.

class Logical {
    mutable int var;

public:
    Logical(): var(0) {}
    void set(int x) const { var = x; }
};

class Bitwise {
    int var;

public:
    Bitwise(): var(0) {}
    void set(int x) const {
        const_cast<Bitwise*>(this)->var = x;
    }
};

const Logical logical; // Not put in read-only.
const Bitwise bitwise; // Likely put in read-only.

int main(void)
{
    logical.set(5); // Well defined.
    bitwise.set(5); // Undefined.
}

See the other answers for more details but I wanted to highlight that it isn't merely for type-saftey and that it affects the compiled result.

风透绣罗衣 2024-07-11 19:36:50

你对它的使用并不是一种 hack,尽管像 C++ 中的许多东西一样,对于那些不想一路返回并标记不应该标记的东西的懒惰程序员来说,可变可以作为非 const 是 const。

Your use of it isn't a hack, though like many things in C++, mutable can be hack for a lazy programmer who doesn't want to go all the way back and mark something that shouldn't be const as non-const.

醉酒的小男人 2024-07-11 19:36:49

mutable 主要用于类的实现细节。 类的用户不需要知道它,因此他认为“应该”的方法是 const 即可。 您的互斥体可变的示例是一个很好的规范示例。

mutable is mainly used on an implementation detail of the class. The user of the class doesn't need to know about it, therefore method's he thinks "should" be const can be. Your example of having a mutex be mutable is a good canonical example.

怂人 2024-07-11 19:36:49

对于逻辑上对用户来说无状态的事物(因此在公共类的 API 中应该有“const”getter)但在底层实现(.cpp 中的代码)中不是无状态的,请使用“可变”。

我最常使用它的情况是无状态“普通旧数据”成员的延迟初始化。 也就是说,在少数情况下,它是理想的,因为此类成员的构建(处理器)或携带(内存)都很昂贵,并且该对象的许多用户永远不会要求它们。 在这种情况下,您希望在后端进行惰性构建以提高性能,因为构建的 90% 的对象根本不需要构建它们,但您仍然需要提供正确的无状态 API 供公共使用。

Use "mutable" when for things that are LOGICALLY stateless to the user (and thus should have "const" getters in the public class' APIs) but are NOT stateless in the underlying IMPLEMENTATION (the code in your .cpp).

The cases I use it most frequently are lazy initialization of state-less "plain old data" members. Namely, it is ideal in the narrow cases when such members are expensive to either build (processor) or carry around (memory) and many users of the object will never ask for them. In that situation you want lazy construction on the back end for performance, since 90% of the objects built will never need to build them at all, yet you still need to present the correct stateless API for public consumption.

空城旧梦 2024-07-11 19:36:49

嗯,是的,这就是它的作用。 我将它用于通过不会逻辑改变类状态的方法修改的成员 - 例如,通过实现缓存来加速查找:

class CIniWrapper
{
public:
   CIniWrapper(LPCTSTR szIniFile);

   // non-const: logically modifies the state of the object
   void SetValue(LPCTSTR szName, LPCTSTR szValue);

   // const: does not logically change the object
   LPCTSTR GetValue(LPCTSTR szName, LPCTSTR szDefaultValue) const;

   // ...

private:
   // cache, avoids going to disk when a named value is retrieved multiple times
   // does not logically change the public interface, so declared mutable
   // so that it can be used by the const GetValue() method
   mutable std::map<string, string> m_mapNameToValue;
};

现在,您必须小心使用它 - 并发问题是一个大问题,因为如果仅使用 const 方法,调用者可能会认为它们是线程安全的。 当然,修改可变数据不应该以任何显着的方式改变对象的行为,我给出的示例可能会违反这一点,例如,如果预期将更改写入磁盘将立即对应用程序可见。

Well, yeah, that's what it does. I use it for members that are modified by methods that do not logically change the state of a class - for instance, to speed up lookups by implementing a cache:

class CIniWrapper
{
public:
   CIniWrapper(LPCTSTR szIniFile);

   // non-const: logically modifies the state of the object
   void SetValue(LPCTSTR szName, LPCTSTR szValue);

   // const: does not logically change the object
   LPCTSTR GetValue(LPCTSTR szName, LPCTSTR szDefaultValue) const;

   // ...

private:
   // cache, avoids going to disk when a named value is retrieved multiple times
   // does not logically change the public interface, so declared mutable
   // so that it can be used by the const GetValue() method
   mutable std::map<string, string> m_mapNameToValue;
};

Now, you must use this with care - concurrency issues are a big concern, as a caller might assume that they are thread safe if only using const methods. And of course, modifying mutable data shouldn't change the behavior of the object in any significant fashion, something that could be violated by the example i gave if, for instance, it was expected that changes written to disk would be immediately visible to the app.

如果没有你 2024-07-11 19:36:49

当类内部有一个变量仅在该类中用于表示互斥锁或锁等信号时,将使用可变。 该变量不会更改类的行为,但为了实现类本身的线程安全性是必需的。 因此,如果没有“mutable”,您将无法拥有“const”函数,因为需要在外界可用的所有函数中更改此变量。 因此,引入 mutable 是为了使成员变量即使是 const 函数也可写。

指定的可变项通知编译器和读者它
是安全的并且预期可以在 const 内修改成员变量
成员函数。

Mutable is used when you have a variable inside the class that is only used within that class to signal things like for example a mutex or a lock. This variable does not change the behaviour of the class, but is necessary in order to implement thread safety of the class itself. Thus if without "mutable", you would not be able to have "const" functions because this variable will need to be changed in all functions that are available to the outside world. Therefore, mutable was introduced in order to make a member variable writable even by a const function.

The mutable specified informs both the compiler and the reader that it
is safe and expected that a member variable may be modified within a const
member function.

红焚 2024-07-11 19:36:49

当您推断允许修改其他常量函数中的数据时,mutable 确实存在。

目的是您可能有一个对对象的内部状态“不执行任何操作”的函数,因此您将函数标记为 const,但您可能确实需要修改不影响其正确功能的方式。

该关键字可以充当编译器的提示——理论上编译器可以将常量对象(例如全局对象)放置在标记为只读的内存中。 mutable 的存在暗示不应该这样做。

以下是声明和使用可变数据的一些正当理由:

  • 线程安全。 声明一个可变的 boost::mutex 是完全合理的。
  • 统计数据。 给定函数的部分或全部参数,计算对函数的调用次数。
  • 记忆化。 计算一些昂贵的答案,然后将其存储以供将来参考,而不是再次重新计算。

mutable does exist as you infer to allow one to modify data in an otherwise constant function.

The intent is that you might have a function that "does nothing" to the internal state of the object, and so you mark the function const, but you might really need to modify some of the objects state in ways that don't affect its correct functionality.

The keyword may act as a hint to the compiler -- a theoretical compiler could place a constant object (such as a global) in memory that was marked read-only. The presence of mutable hints that this should not be done.

Here are some valid reasons to declare and use mutable data:

  • Thread safety. Declaring a mutable boost::mutex is perfectly reasonable.
  • Statistics. Counting the number of calls to a function, given some or all of its arguments.
  • Memoization. Computing some expensive answer, and then storing it for future reference rather than recomputing it again.
所谓喜欢 2024-07-11 19:36:49

它在隐藏内部状态(例如缓存)的情况下非常有用。 例如:

class HashTable
{
...
public:
    string lookup(string key) const
    {
        if(key == lastKey)
            return lastValue;

        string value = lookupInternal(key);

        lastKey = key;
        lastValue = value;

        return value;
    }

private:
    mutable string lastKey, lastValue;
};

然后您可以让一个 const HashTable 对象仍然使用其 lookup() 方法,该方法会修改内部缓存。

It's useful in situations where you have hidden internal state such as a cache. For example:

class HashTable
{
...
public:
    string lookup(string key) const
    {
        if(key == lastKey)
            return lastValue;

        string value = lookupInternal(key);

        lastKey = key;
        lastValue = value;

        return value;
    }

private:
    mutable string lastKey, lastValue;
};

And then you can have a const HashTable object still use its lookup() method, which modifies the internal cache.

趁年轻赶紧闹 2024-07-11 19:36:49

您对 boost::mutex 的使用正是该关键字的用途。 另一个用途是用于内部结果缓存以加速访问。

基本上,“可变”适用于不影响对象的外部可见状态的任何类属性。

在您问题的示例代码中,如果 did_ 的值影响外部状态,则 mutable 可能不合适,这取决于 ... 中的内容; 部分。

Your use with boost::mutex is exactly what this keyword is intended for. Another use is for internal result caching to speed access.

Basically, 'mutable' applies to any class attribute that does not affect the externally visible state of the object.

In the sample code in your question, mutable might be inappropriate if the value of done_ affects external state, it depends on what is in the ...; part.

鲜肉鲜肉永远不皱 2024-07-11 19:36:49

Mutable 用于将特定属性标记为可在 const 方法中修改。 这是它的唯一目的。 使用它之前请仔细考虑,因为如果您更改设计而不是使用 mutable,您的代码可能会更干净且更具可读性。

http://www.highprogrammer.com/alan/rants/mutable.html

所以如果上面的疯狂不是啥
mutable 是用来做什么的,它是用来做什么的? 这是
微妙的情况:可变的是
对象在逻辑上是这样的情况
恒定,但在实践中需要
改变。 这些案例为数不多且遥远
之间,但它们存在。

作者给出的示例包括缓存和临时调试变量。

Mutable is for marking specific attribute as modifiable from within const methods. That is its only purpose. Think carefully before using it, because your code will probably be cleaner and more readable if you change the design rather than use mutable.

http://www.highprogrammer.com/alan/rants/mutable.html

So if the above madness isn't what
mutable is for, what is it for? Here's
the subtle case: mutable is for the
case where an object is logically
constant, but in practice needs to
change. These cases are few and far
between, but they exist.

Examples the author gives include caching and temporary debugging variables.

草莓酥 2024-07-11 19:36:49

mutable 关键字是一种刺破覆盖在对象上的 const 面纱的方法。 如果您有一个指向对象的 const 引用或指针,则您不能以任何方式修改该对象,除了将其标记为可变的时间和方式。

使用 const 引用或指针,您将受到以下限制:

  • 只能读取任何可见数据成员的权限
  • 只能调用标记为 const 的方法。

mutable 异常使得您现在可以编写或设置标记为 mutable 的数据成员。 这是唯一外部可见的差异。

在内部,您可见的那些 const 方法也可以写入标记为 mutable 的数据成员。 本质上,常量面纱已被全面刺破。 API 设计者完全有责任确保 mutable 不会破坏 const 概念,并且仅在有用的特殊情况下使用。 mutable 关键字很有帮助,因为它清楚地标记了受这些特殊情况影响的数据成员。

实际上,您可以在整个代码库中大量使用 const(您本质上是想用 const“疾病”“感染”您的代码库)。 在这个世界中,指针和引用都是 const,几乎没有例外,从而产生更容易推理和理解的代码。 对于一个有趣的题外话,请查阅“引用透明度”。

如果没有 mutable 关键字,您最终将被迫使用 const_cast 来处理它允许的各种有用的特殊情况(缓存、引用计数、调试数据等)。 不幸的是,const_castmutable 更具破坏性,因为它强制 API 客户端 破坏对象的 const 保护他(她)正在使用。 此外,它还会导致广泛的 const 破坏:使用 const 指针或引用进行 const_cast 允许对可见成员进行不受约束的写入和方法调用访问。 相比之下,mutable 要求 API 设计者对 const 异常进行细粒度控制,通常这些异常隐藏在私有操作的 const 方法中。数据。

(注意,我多次提到数据和方法可见性。我谈论的是标记为公共与私有或受保护的成员,这是讨论的完全不同类型的对象保护此处。)

The mutable keyword is a way to pierce the const veil you drape over your objects. If you have a const reference or pointer to an object, you cannot modify that object in any way except when and how it is marked mutable.

With your const reference or pointer you are constrained to:

  • only read access for any visible data members
  • permission to call only methods that are marked as const.

The mutable exception makes it so you can now write or set data members that are marked mutable. That's the only externally visible difference.

Internally those const methods that are visible to you can also write to data members that are marked mutable. Essentially the const veil is pierced comprehensively. It is completely up to the API designer to ensure that mutable doesn't destroy the const concept and is only used in useful special cases. The mutable keyword helps because it clearly marks data members that are subject to these special cases.

In practice you can use const obsessively throughout your codebase (you essentially want to "infect" your codebase with the const "disease"). In this world pointers and references are const with very few exceptions, yielding code that is easier to reason about and understand. For a interesting digression look up "referential transparency".

Without the mutable keyword you will eventually be forced to use const_cast to handle the various useful special cases it allows (caching, ref counting, debug data, etc.). Unfortunately const_cast is significantly more destructive than mutable because it forces the API client to destroy the const protection of the objects (s)he is using. Additionally it causes widespread const destruction: const_casting a const pointer or reference allows unfettered write and method calling access to visible members. In contrast mutable requires the API designer to exercise fine grained control over the const exceptions, and usually these exceptions are hidden in const methods operating on private data.

(N.B. I refer to to data and method visibility a few times. I'm talking about members marked as public vs. private or protected which is a totally different type of object protection discussed here.)

腹黑女流氓 2024-07-11 19:36:48

它允许区分按位常量和逻辑常量。 逻辑常量是指对象不会以通过公共接口可见的方式更改,例如锁定示例。 另一个例子是一个类,它在第一次请求时计算一个值,并缓存结果。

由于 c++11 mutable 可用于 lambda 来表示通过值捕获的内容是可修改的(默认情况下它们是不可修改的):

int x = 0;
auto f1 = [=]() mutable {x = 42;};  // OK
auto f2 = [=]()         {x = 42;};  // Error: a by-value capture cannot be modified in a non-mutable lambda

It allows the differentiation of bitwise const and logical const. Logical const is when an object doesn't change in a way that is visible through the public interface, like your locking example. Another example would be a class that computes a value the first time it is requested, and caches the result.

Since c++11 mutable can be used on a lambda to denote that things captured by value are modifiable (they aren't by default):

int x = 0;
auto f1 = [=]() mutable {x = 42;};  // OK
auto f2 = [=]()         {x = 42;};  // Error: a by-value capture cannot be modified in a non-mutable lambda
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文