【问题标题】:Should Mutex's lock/unlock functions be "const"?Mutex 的锁定/解锁功能应该是“const”吗?
【发布时间】:2010-08-26 08:58:54
【问题描述】:

我正在维护一个包含 Mutex 类的库。我无法决定此类公开的 lock() 和 unlock() 函数是否应该是 const 。我在网上找了类似的代码,两种实现都可以找到。

第一个实现,lock() 和 unlock() 不是 const。这意味着在 const 函数中使用 Mutex 类的人应该付出额外的努力来调用 Mutex 的函数:

class Mutex
{
public:
   void lock();
   void unlock();
};

class Foo
{
public:
   void getBar(Bar& bar) const
   {
      m_mutex.lock();
      bar = m_bar;
      m_mutex.unlock();
   }

private:
   // Mutable, responsability of the user (or we could use a const_cast when needed)
   mutable Mutex m_mutex;
   Bar   m_bar;
};

第二个实现,lock() 和 unlock() 是 const,即使这听起来不是很自然(因为我们修改了 Mutex 实例),但是用户在他的 const 之一中调用这些函数时不必费心功能:

class Mutex
{
public:
   void lock() const;
   void unlock() const;
};

class Foo
{
public:
   void getBar(Bar& bar) const
   {
      m_mutex.lock();
      bar = m_bar;
      m_mutex.unlock();
   }

private:
   Mutex m_mutex;
   Bar   m_bar;
};

您更喜欢哪种解决方案?希望您的意见能帮助我做出决定。

【问题讨论】:

  • 当我有一个函数接受一个参数,该参数将以 getBar 中的方式进行修改,我喜欢称它为 fillBar,这样它的作用就没有歧义了。

标签: c++ multithreading api


【解决方案1】:

mutable 就是为这种东西而生的。即,mutable 适用于不参与对象的逻辑常量 的事物。 (如果一个值是“逻辑上恒定的”,则意味着“主要”值是恒定的;对象的值,从外部看,不能改变。)

对象的值独立与互斥锁的状态无关(它只是提供一致性的实现细节,它的状态在类之外是未知的),这是你的一个好兆头互斥量应该是mutable


注意,你不应该走const_cast 路线。 This leads to undefined behavior 如果你这样做了:

const Foo f;

Bar b;
f.getBar(b); // takes const off of mutex, modifies (bang you're dead)

【讨论】:

  • 这是否意味着如果一个类有一个可变成员,编译器/链接器会以不同的方式处理它(即,与“普通” const 类不同,它的实例不能放入 const 地址空间) ?
  • @Péter:是的。 §3.9.3/3:“const 限定的类对象的每个非静态、非可变、非引用数据成员都是 const 限定的……”所以mutable 成员没有获得 const 限定.
【解决方案2】:

我更喜欢在客户端代码中创建Mutex mutable 的第一个选项。通过这种方式,类的用户明确地知道他正在从他的 const 函数调用一个非常量函数。事实上,如果你看到 CMutex 类的 MFC 实现,你会发现 LockUnlock 方法是非常量的。

【讨论】:

【解决方案3】:

首选第一个实现:const 条件指的是 logical const-nessbinary const-ness:互斥锁是一个辅助类 - 它实际上不是更改客户端代码的逻辑状态,但更改它确实会更改客户端代码的二进制状态。

因此,互斥体应在客户端代码中设置为mutable

如果您在代码中缓存一些昂贵的操作结果,也会出现同样的情况:您将 chache 变量声明为可变的,因此在计算它时您不需要丢弃类的 const-ness。

【讨论】:

    【解决方案4】:

    如果你想从 Mutex 类派生类,那么将这两个函数设为 const 是(有些)意义的,那么你可以在派生类自己的 const 函数中调用它。

    像这样:

    class Foo : public Mutex {
    public:
        void
            bar() const { lock(); doSomething(); unlock(); }
    }
    

    【讨论】:

    • 我认为这是一个不好的例子。使用继承向类中添加可以通过组合轻松添加的内容是不好的风格恕我直言。
    • @Martin:这并不比在封装中欺骗mutable 差:这两种方法都有利有弊。在这种情况下,这完全取决于 Mutex 类的使用模式,以及它对程序员 IMO 的感觉是否更自然。
    猜你喜欢
    • 2013-02-02
    • 2012-10-24
    • 2012-02-08
    • 2021-10-30
    • 1970-01-01
    • 2013-11-14
    • 1970-01-01
    • 1970-01-01
    • 2010-12-26
    相关资源
    最近更新 更多