【发布时间】:2013-08-09 02:08:26
【问题描述】:
最近我将我的 getter 和 setter 编写为(注意:真正的类在 getter/setter 中做更多的事情):
struct A {
const int& value() const { return value_; } // getter
int& value() { return value_; } // getter/setter
private:
int value_;
};
这允许我执行以下操作:
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
这种方法不允许我:
- 验证设置器的输入(这是一个很大的 BUT),
- 在 const 成员函数中按值返回(因为我希望编译器捕获对 const 对象的赋值:
ca.value() = 2)。更新:请参阅下面的 curacan 答案。
但是,我仍然经常使用它,因为
- 大多数时候我不需要那个,
- 这使我可以将类的实现细节与其接口分离,这正是我想要的。
例子:
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 :)
};
现在回答问题:
- 我没有看到这种方法的其他缺点吗?
- 为什么人们更喜欢:
- getter/setter 方法名称不同?
- 将值作为参数传递? 只是为了验证输入还是有其他主要原因?
【问题讨论】:
-
一个缺点是打字比公开数据成员要多,而且回报不多。
-
我相信人们通常出于您所说的确切原因将值作为参数传递 - 输入验证。以及安全性,以及您能想到的任何其他用途。您可以确保对象具有更改某些值的“权限”,并进行验证以确保它们没有输入无效数据。
-
...从语义上讲,您的 get-setter 与简单的公共成员之间没有区别。如果人们想要这种行为,他们只需创建一个公共成员,否则,我们使用更标准的 get set(value)。
-
在生产代码中,使用
value() = 3的一个主要缺点是它需要更多的消化才能理解 value 作为 setter 被重载并返回可以修改的引用。当您使用现有 API 维护或编写新代码时,您希望可读性简单快速。get()和set(value)明确且完全不言自明(即不需要 cmets)。 -
这似乎是一种非常混乱的方式来做一些应该很简单的事情。如果您需要“验证输入”,因为您的成员可能彼此不同步(例如,日期结构,其中“递增”日期可能需要更改月份和/或年份),那么您可能应该提供这些操作(例如increment_day()),并且不能访问更改个别成员。如果单个成员不必同步,但需要在特定范围内,则最好将这些成员转换为类,并让他们自己进行验证。