为什么人们编写返回非常量引用的私有字段 getter?

Why do people write private-field getters returning a non-const reference?

我们都同意 public 变量不利于封装等等。但是,我注意到很多代码都在做这种事情:

class foo {
private:
    int integer_;
    string someString_;
    // other variables
public:
    int& integer() { return integer_; }
    string& someString() { return someString_; }
    // other "functions"
}

int main() {
    foo f;
    f.integer() = 10;
    f.someString() = "something";
    return 0;
}

我看到很多地方都在使用这个,但我不明白为什么。基本上它 returns 对数据的引用,因此直接将其暴露给外部。所以封装并没有真正实现,从任何角度来看都不是。

为什么这么常用?

我强烈建议不要返回对私有变量的 非常量 引用。不是因为它破坏了封装,而是因为它是不必要的:为什么不首先创建变量 public

打破封装是不好的,是的,但这并不意味着每个变量都应该是 private。有些变量是要从用户那里读取和修改的,因此将它们设置为 public 是有意义的。比如拿std::pair,它有2个public成员变量,firstsecond。这不是坏习惯。

唯一没有意义的时候是不应该写入变量的时候。那会很糟糕,因为它实际上会破坏封装并使整个程序难以调试。

有一个反复出现的咒语,即 getter/setter 函数应该用于封装您的数据。因此,许多(没有经验或喝咖啡的)程序员认为他们应该使用类似的东西:

int& integer() { return integer_; }

但这与简单地写没有太大区别:

class foo {
public: // <<<
    int integer_;
    string someString_;
    // ...
};

好吧,它添加了一个函数调用,但您无法控制客户端对引用执行的操作。


如果你真的想提供一个getter函数写:

const int& integer() const { return integer_; }

对应的setter函数如下所示:

void integer(const int& value) {
    integer_ = value;
}

考虑到 class 的定义可能会随着时间的推移而演变,我不得不部分不同意@πìνταῥεῖ 和@Rakete1111 的回答。

虽然这些 getter 方法通常是由刚听过 "no exposing members" 咒语的人编写的,但它们也可以有合法用途:

  1. getter 方法可能 稍后 被修改以在 return 引用之前包含某种有效性检查或资源分配代码 - 直接访问数据成员不允许。虽然这意味着更改 class 的代码,但不需要更改 class 用户 的代码。此外,如果 getter 的实现未在 class header 中公开,则可能甚至不需要重新编译 class 用户代码。 注意:这样做可能是其他一些糟糕的设计选择的标志。
  2. getter 方法可能会被 subclass 重写(在这种情况下,它通常是一个虚方法),方法与上面类似。
  3. getter 方法稍后可能会将其 return 类型替换为原始数据成员类型的 proxy,而不是对实际成员的引用 - 甚至可能不再存在。想想 vector<bool> 是如何工作的;当你调用它的 operator[] 时,你不会得到 boolean&,你会得到某种代理,当分配或 assigned-to 时,它会进行适当的位提取或设置。
  4. A non-const getter 不适用于 non-const 个实例。所以实际上它确实 限制访问 相对于完全公开成员。 OP 示例 class 的作者是否真的打算这是一个不同的问题...

总结一下:"dummy"non-const-referencegetter可以作为其他的stub,有意义, 代码.

也就是说,将 getter return 设为常量引用或值通常是个好主意。或者只是在适当的情况下公开该字段(其中也有一些)。

此构造可用于调试目的。

如果您有一个 public 变量,则无法轻松监控其使用情况。 将其转换为一对私有变量和返回方法的引用将允许您放置断点 and/or 记录调用。

但是,将 getter 和 setter 分开会更好地达到相同的目的,因此这只是优于普通 public 变量的优势。

我写过一次。我计划稍后返回并用返回堆栈对象的东西替换字段 getter 到可以转换为原始类型并由原始类型分配给的东西。这让我可以稍后返回并拦截所有作业以进行验证。

有点过分的技巧。项目中的 None 的其他编码人员完全可以理解它。整个堆栈都被撕掉了。