如何编写 C 的 getter 和 setter

新手上路,请多包涵

如果我需要编写一个 setter 和/或 getter,我会这样写:

 struct X { /*...*/};

class Foo
{
private:
    X x_;

public:
    void set_x(X value)
    {
        x_ = value;
    }
    X get_x()
    {
        return x_;
    }
};

但是我听说这是编写 setter 和 getter 的 _Java 风格_,我应该用 C++ 风格编写它。此外,有人告诉我这是低效的,甚至是不正确的。这意味着什么?如何在 C++ 中编写 setter 和 getter?


假设对 getter 和/或 setter 的需求是合理的。例如,也许我们在 setter 中做一些检查,或者我们只写 getter。

有很多关于不需要 getter 和 setter 的讨论。虽然我同意这里所说的大部分内容,但我仍然主张需要知道如何惯用地编写此类方法,因为有正当理由认为 getter 和 setter 是正确的解决方案。乍一看,它们可能并不像 setter 或 getter,但它们确实如此,或者至少适用于编写它们的模式。

例如:

  • 获取向量的大小。您不想公开数据成员,因为它需要是只读的。

  • Getter 和 setter 不需要只公开一个数据成员。考虑获取和设置数组的元素。那里有逻辑,您不能只公开一个数据成员,首先没有要公开的数据成员。它仍然是你无法避免的 getter/setter 对:

   class Vector
  {
      void set_element(std::size_t index, int new_value);
      int get_element(std::size_t index);
  };

了解编写 getter 和 setter 的 C++ 惯用方式将使我能够以 C++ 惯用方式编写上述 get_element / set_element

原文由 bolov 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.2k
2 个回答

标准库中出现了两种不同形式的“属性”,我将其分类为“面向身份”和“面向价值”。您选择哪个取决于系统应如何与 Foo 交互。两者都不是“更正确”。

面向身份

class Foo
{
     X x_;
public:
          X & x()       { return x_; }
    const X & x() const { return x_; }
}

这里我们返回一个对底层 X 成员的 _引用_,它允许调用站点的双方观察对方发起的更改。 X 成员对外界可见,大概是因为它的身份很重要。乍一看似乎只有“获取”属性的一面,但如果 X 是可分配的,则情况并非如此。

  Foo f;
 f.x() = X { ... };

价值导向

class Foo
{
     X x_;
public:
     X x() const { return x_; }
     void x(X x) { x_ = std::move(x); }
}

这里我们返回一个 X 成员的 _副本_,并接受一个 副本 来覆盖。以后任何一方的更改都不会传播。大概在这种情况下,我们只关心 x 的 _值_。

原文由 Caleth 发布,翻译遵循 CC BY-SA 4.0 许可协议

您的主要错误是,如果您不在 API 参数和返回值中使用引用,那么您 可能会 冒着在两个 get/set 操作中执行不需要的副本的风险(“可能”,因为如果您使用优化器,您的编译可能能够避免这些副本)。

我会把它写成:

 class Foo
{
private:
    X x_;
public:
    void x(const X &value) { x_ = value; }
    const X &x() const { return x_; }
};

这将保持 const 正确性,这是 C++ 的一个非常重要的特性,并且它与旧的 C++ 版本兼容(另一个答案需要 c++11)。

您可以将此类用于:

 Foo f;
X obj;
f.x(obj);
X objcopy = f.x(); // get a copy of f::x_
const X &objref = f.x(); // get a reference to f::x_

我发现 get/set 的使用对于 _ 或驼峰式大小写都是多余的(即 getX()、setX()),如果你做错了什么,编译器会帮助你解决问题。

如果要修改内部的 Foo::X 对象,还可以添加 x() 的第三个重载:

 X &x() { return x_; }

..通过这种方式,您可以编写如下内容:

 Foo f;
X obj;
f.x() = obj; // replace inner object
f.x().int_member = 1; // replace a single value inside f::x_

但我建议你避免这种情况,除非你真的需要经常修改内部结构(X)。

原文由 gabry 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题