简单的C++ getter/setters

gnz*_*lbg 5 c++ class

最近我正在写我的getter和setter(注意:真正的类在getter/setter中做更多的事情):

struct A {
  const int& value() const { return value_; } // getter
        int& value()       { return value_; } // getter/setter
 private:
  int value_;
};
Run Code Online (Sandbox Code Playgroud)

这允许我做以下事情:

auto a = A{2}; // non-const object a

// create copies by "default" (value always returns a ref!):
int b = a.value();  // b = 2, is a copy of value :)
auto c = a.value(); // c = 2, is a copy of value :)

// create references explicitly:
auto& d = a.value();  // d is a ref to a.value_ :)
decltype(a.value()) e = a.value(); // e is a ref to a.value_ :)
a.value() = 3; // sets a.value_ = 3 :)

cout << b << " " << c << " " << d << " " << e << endl; // 2 2 3 3

const auto ca = A{1};
const auto& f = ca.value();  // f is a const ref to ca.value_ :)
auto& g = ca.value(); // no compiler error! :(
// g = 4; // compiler error :)
decltype(ca.value()) h = ca.value(); // h is a const ref to ca.value_ :)
//ca.value() = 2; // compiler error! :)

cout << f << " " << g << " " << h << endl; // 1 1 1
Run Code Online (Sandbox Code Playgroud)

这种方法不允许我:

  • 验证setter的输入(这是一个很大的BUT),
  • 在const成员函数中按值返回(因为我希望编译器捕获对const对象的赋值:) ca.value() = 2.更新:请参阅下面的cluracan答案.

但是,我仍然使用它很多,因为

  • 大多数时候我不需要那个,
  • 这允许我将我的类的实现细节与其接口分离,这正是我想要的.

例:

struct A {
  const int& value(const std::size_t i) const { return values_[i]; }
        int& value(const std::size_t i)       { return values_[i]; }
  private:
   std::vector<int> values_; 
   // Storing the values in a vector/list/etc is an implementation detail.
   // - I can validate the index, but not the value :(
   // - I can change the type of values, without affecting clients :)
 }; 
Run Code Online (Sandbox Code Playgroud)

现在问题:

  • 我没有看到这种方法还有其他任何缺点吗?
  • 为什么人们更喜欢:
    • getter/setters方法有不同的名字?
    • 将值作为参数传递?仅用于验证输入还是有其他主要原因?

Mar*_*k B 6

  • 通常使用访问器/变换器是一种设计气味,您的类公共接口是不完整的.通常来说,您需要一个有用的公共接口,它提供有意义的功能而不是简单地获取/设置(这比我们在C中使用结构和函数更好一两步).每次你想写一个mutator,并且很多时候你想写一个访问器,先退后一步,问问自己"do I *really* need this?".
  • 只是习惯用户可能不准备期望这样的功能,所以它会增加维护者的时间来修改你的代码.
  • 同名的方法几乎与公共成员相同:在这种情况下只使用公共成员.当方法执行两个不同的操作时,将它们命名为两个不同的东西.
  • 通过非const引用返回的"mutator"将允许各种各样的别名问题,其中有人将成员的别名隐藏起来,依赖于它以后存在.通过使用单独的setter函数,可以防止人们对您的私有数据进行别名.


rab*_*sky 5

这种方法不允许我:

  • 在const成员函数中按值返回(因为我希望编译器捕获对const对象的赋值ca.value()= 2).

我不明白你的意思.如果你的意思是我认为你的意思 - 你会感到惊喜:)只是试着让const成员按价值回归,看看你是否可以ca.value()=2......

但我的主要问题是,如果你想要某种输入验证,为什么不使用专用的setter和专用的getter

struct A {
  int  value() const { return value_; } // getter
  void value(int v)  { value_=v; }      // setter
 private:
  int value_;
};
Run Code Online (Sandbox Code Playgroud)

它甚至会减少输入的数量!(一个'=')当你设置.唯一的缺点是你不能通过引用传递值来修改它.

关于编辑后的第二个示例,使用vector- 使用getter/setter比原始示例更有意义,因为您想要访问值(允许用户更改值)但不是向量(不要使用)不希望用户能够改变矢量的大小.

所以即使在第一个例子中我真的建议公开成员,但在第二个例子中它显然不是一个选项,如果不需要输入验证,使用这种形式的getter/setter确实是一个不错的选择.

此外,当我有类似你的第二种类型(使用向量)时,我喜欢访问beginend迭代器.这样可以更灵活地使用标准工具使用数据(同时仍然不允许用户更改vector大小,并允许轻松更改容器类型)

另一个好处是随机访问迭代器有一个operator[](像指针),所以你可以做

vector<int>::iterator       A::value_begin()     {return values_.begin();}
vector<int>::const_iterator A::value_begin()const{return values_.begin();}
...
a.value_begin()[252]=3;
int b=a.value_begin()[4];
vector<int> c(a.value_begin(),a.value_end())
Run Code Online (Sandbox Code Playgroud)

(虽然它可能很难看,除此之外你仍然想要你的吸气剂/安装者)