如何重构冗余的getter/setter方法



我有一个具有互斥锁保护成员的现有类,看起来像这样(类MembersNeedThreadSafe)…

// Just fake a mutex guard interface.  Obviously doesn't do anything
class Mutex
{
  public:
    struct Guard
    {
      Guard(Mutex & M) { };
    };
};
// This is the class I want to redesign.
class MembersNeedThreadSafe {
  Mutex M;
  int i;
  double k;
  // And a dozen more variables
  public:
  void SetI (int foo) { Mutex::Guard G(M); i = foo; }
  int GetI (void) { Mutex::Guard G(M); return i; }
  void SetK (double foo) { Mutex::Guard G(M); k = foo; }
  double GetK (void) { Mutex::Guard G(M); return k; }
  // And two dozen more methods
};
int main (void) {
  MembersNeedThreadSafe bar;
  bar.SetI(5);
  bar.SetK(6.0);
  double d = bar.GetK();
  return 0;
}

我想重构类MembersNeedThreadSafe为一个更像这样的设计,但它不编译,抱怨非静态数据成员的无效使用。

template <typename T, Mutex & M> class LockedVar {
  typedef LockedVar<T, M> my_type;
  T value;
  public:
  void Set(T const & foo) { Mutex::Guard G(M); value = foo; }
  T const & Get (void) { Mutex::Guard G(M); return value; }
};
// I want the class to look like this...
class MembersNeedThreadSafe {
  Mutex M;
  public:
  LockedVar <int, M> i;
  LockedVar <double, M> k;
  // And a dozen more variables
};
// This allows the code to run.
int main (void) {
  MembersNeedThreadSafe bar;
  bar.i.Set(5);
  bar.k.Set(6.0);
  double d = bar.k.Get();
  return 0;
}

所以…我如何重构第一个代码块的MembersNeedThreadSafe类,这样我就不必为每个成员写一个冗余的getter和setter方法?

附录:我知道我可以使用这样的设计…

template <typename T> class LockedVar {
  typedef LockedVar<T> my_type;
  T value;
  Mutex & M;
  public:
  LockedVar (Mutex & foo) : M(foo) { }
  void Set(T const & foo) { Mutex::Guard G(M); value = foo; }
  T const & Get (void) { Mutex::Guard G(M); return value; }
};

但是当我这样做时,sizeof(int) == 4,而sizeof(LockedVar) == 16在我的编译器(gcc 4.8.2)中,这给我抛出了一种危险信号。似乎我应该能够通过使用互斥锁作为模板参数来解决这个问题,如果可能的话,我想知道如何做到这一点。

另一个选择是稍微修改一下LockedVar:

template <typename T> class LockedVar {
  typedef LockedVar<T> my_type;
  T value;
  public:
  void Set(T const & foo, Mutex & M ) { Mutex::Guard G(M); value = foo; }
  T const & Get (Mutex & M) { Mutex::Guard G(M); return value; }
};

同样,如果你要反复重复这个,尽管它可能不是很好看,你可以创建一个宏:

#define IMPL_SET_GET( t, x ) 
    t x; 
    void Set##x (int foo) { Mutex::Guard G(M); x = foo; } 
    int Get##x (void) { Mutex::Guard G(M); return x; }
// This is the class I want to redesign.
class MembersNeedThreadSafe {
  Mutex M;

  // And a dozen more variables
  public:
      IMPL_SET_GET( int, i );
      IMPL_SET_GET( int, k );
  // And two dozen more methods
};

您的设计一开始就有问题,锁的粒度允许如下交互:

// Thread 1                           // Thread 2
if (x.GetI() != 0) {
                                      x.SetI(0);
    return y / x.GetI();
}

一般来说,你更喜欢:

    锁定对象
  1. 执行所有需要自动完成的动作
  2. 解锁对象

一个简单的方法:

class Data {
public:
    friend class Behavior;
    Data(): i(3), k(7) {}
private:
    std::mutex mutex;
    int i;
    int k;
}; // class Data
class Behavior {
public:
    explicit Behavior(Data& data): ref(data), guard(ref.mutex) {}
    int GetI() const { return ref.i; }
    void SetI(int i) { ref.i = i; }
    int GetK() const { return ref.k; }
    void SetK(int k) { ref.k = k; }
private:
    Data& ref;
    std::unique_lock<std::mutex> guard;
}; // class Behavior

注意互斥锁只锁定一次(在Behavior的构造函数中),您不必在每个getter和setter上重复它。

最新更新