返回访问器的引用是否惯用?

发布于 2024-10-20 20:18:07 字数 360 浏览 7 评论 0原文

在 C++ 中,可以创建一个返回对私有字段的引用的访问器。

class Cls {
    private:
        int _attr;
    public:
        int& attr() { return _attr; }
};

这样就可以这样访问该属性:

// set
c.attr() = 4;

// get
cout << c.attr() << endl;

这种访问器风格是否惯用/良好实践?一般的 C++ 程序员看到这种风格的访问器会感到惊讶吗? (提示:我第一次看到这个时很惊讶,但有点喜欢这种风格)

In C++, it is possible to create an accessor which returns a reference to a private field.

class Cls {
    private:
        int _attr;
    public:
        int& attr() { return _attr; }
};

such that the attribute can be accessed as such:

// set
c.attr() = 4;

// get
cout << c.attr() << endl;

Is this style of accessor idiomatic/good practice? Would an average C++ programmer be surprised seeing this style of accessor? (Hint: I was surprised the first time I saw this, but kind of liked the style)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

给我一枪 2024-10-27 20:18:07

假设您需要保证:

template <int Base>
class Digit {
  private:
    int attr_; // leading underscore names are reserved!
  public:
    int attr() const { return attr_; }
    void attr(int i) { attr_ = i % Base; } // !
};

在这种情况下,您无法返回引用,因为无法保证用户不会以您不想要的方式修改它。这是使用 setter 的主要原因 - 您可以(现在或以后)过滤输入以确保它是可以接受的。通过参考,您必须盲目接受用户分配给您的会员的内容。既然如此,为什么不直接公开呢?

Suppose you needed guarantees:

template <int Base>
class Digit {
  private:
    int attr_; // leading underscore names are reserved!
  public:
    int attr() const { return attr_; }
    void attr(int i) { attr_ = i % Base; } // !
};

In that case, you can't return a reference, since there's no way to guarantee that the user won't modify it in a way that you don't want. This is the main reason for using a setter - you can (now or at a later date) filter the input to make sure it's acceptable. With a reference you have to blindly accept what the user assigns to your member. At that point, why not just make it public?

夜司空 2024-10-27 20:18:07

不,只要您还提供 const 重载,该代码就不足为奇了:

class Cls {
    int _attr;
public:
    int& attr() { return _attr; }
    int const& attr() const { return _attr; }
};

但是,出于 Chris Lutz 和 Mark B 提到的原因,我会考虑以下惯用语:

class Cls {
    int _attr;
public:
    int const& attr() const { return _attr; }
    void attr(int i) { _attr = i; }
};

No, that code would be unsurprising, as long as you also provide a const overload:

class Cls {
    int _attr;
public:
    int& attr() { return _attr; }
    int const& attr() const { return _attr; }
};

However, I would consider the following idiomatic, for the reasons mentioned by Chris Lutz and Mark B:

class Cls {
    int _attr;
public:
    int const& attr() const { return _attr; }
    void attr(int i) { _attr = i; }
};
青芜 2024-10-27 20:18:07

一般来说,对于不平凡的类(例如可以简单地定义为struct的类),为特定属性提供访问器/修改器可能是一种代码味道。一般来说,类应该倾向于保持其内部状态:Internal。如果您提供对内部状态的非常量引用,那么突然间您根本无法控制类不变量。这不仅会使调试变得更加困难,因为可能的状态更改的范围是整个项目,它还会阻止您更改类的内部结构,因为它们实际上既是状态又是用户 API。

相反,您可能应该设计一些方法,在您的类上执行有意义的工作,同时维护类不变量。在某些情况下,提供对特定状态变量的只读访问完全没问题,而在其他情况下,您只想以另一种方式提供对最终状态的访问。

In general, for non-trivial classes (for example ones that can be defined simply as a struct) providing accessors/mutators to specific attributes is probably a code smell. Generally, classes should tend to keep their internal state just that: Internal. If you provide a non-const reference to internal state then all of a sudden you have no control over class invariants at all. This will not only make debugging significantly harder since the scope of possible state changes is the entire project, it prevents you from ever changing the internals of your class because they're actually both state AND user API.

Instead you should probably devise methods that perform meaningful work on your class while maintaining the class invariants. In some cases providing read-only access to specific state variables is perfectly fine while in others you only want to provide access to end state in another way.

无戏配角 2024-10-27 20:18:07

这样做完全违背了将其设为私有的目的。

访问器的目的是公开内部状态,以便当外部代码尝试修改状态时,类可以保持不变量(通过仅允许读访问,或者在修改状态之前验证写访问);它们通常看起来像这样

int  attr() const {return _attr;}
void attr(int a)  {_attr = a;}

:如果将来您需要限制属性可以采用的值,或者更改其内部存储的方式,那么您可以修改它们的实现,而无需更改类接口。

如果没有必要(并且您已经决定永远没有必要),请将其公开;将其公开为引用只会带来几行不必要的代码和使用它时的奇怪语法。

Doing so completely defeats the purpose of making it private in the first place.

The purpose of an accessor is to expose the internal state in such a way that the class can maintain invariants when external code tries to modify the state (either by only allowing read access, or by validating write access before modifying the state); they will usually look something like

int  attr() const {return _attr;}
void attr(int a)  {_attr = a;}

If, in the future, you need to constrain the values that the attribute can take, or change the way it is stored internally, then you can modify the implementation of these without changing the class interface.

If this is not necessary (and you have decided that it will never be necessary), just make it public; exposing it as a reference gains nothing but a few lines of unnecessary code and a quirky syntax when using it.

孤独岁月 2024-10-27 20:18:07

这取决于。如果类的作用是包含这样的对象(例如
容器类),那么它非常惯用,并且是正常的做法
事物。然而,在大多数其他情况下,人们认为最好
使用 getter 和 setter 方法。不一定命名为 getXxx 和
setXxx——我见过的最常见的命名约定使用 m_attr
属性的名称,以及简单的 attr 两个属性的名称
getter 和 setter。 (运算符重载会在它们之间进行选择
根据参数的数量。)

--
詹姆斯·坎泽

It depends. If the role of the class is to contain such objects (e.g.
a container class), then its very idiomatic, and the normal way of doing
things. In most other cases, however, it is considered preferrable to
use getter and setter methods. Not necessarily named getXxx and
setXxx---the most frequent naming convention I've seen uses m_attr for
the name of the attribute, and simply attr for the name of both the
getter and the setter. (Operator overloading will choose between them
according to the number of arguments.)

--
James Kanze

一绘本一梦想 2024-10-27 20:18:07

返回对 POD 成员的引用通常不太常见。我想读者一定会期待有一个二传手来达到这个目的。但是,如果您这样做,请不要忘记为 const 情况也重载它。

Returning references to POD members is generally not too common. I think must readers would expect a setter for this purpose. However, if you do it, don't forget to overload it for the const case as well.

在巴黎塔顶看东京樱花 2024-10-27 20:18:07

这种情况比较常见,但风格很糟糕。

例如,如果您查看过 STL,您会注意到生成 const&& 到内部状态的类通常是普通容器,并且只提供这种对您实际存储在其中的内容的访问。显然,您无法直接修改诸如 size 之类的属性或二叉树的内部节点。

提供对元素的直接访问会破坏封装。您不仅会丢失与这些元素有关的所有类不变量,而且还使它们成为 API 的一部分,因此在不更新所有客户端的情况下无法更改类的实现。

就我而言,它们是 C++ 中的两种类型:

struct BigBag {
  Foo _foo;
  Bar _bar;
  FooBar _foobar;
};

class MyClass {
public:
  explicit MyClass(int a, int b);

  int getSomeInfo();

  void updateDetails(int a, int b);
private:
  // no peeking
};

也就是说:要么它只是相关元素的集合,在这种情况下,一切都是公共的,你就完成了,要么有类不变量和/或你'关心它的 API,因为有很多客户端代码,在这种情况下,您根本不会公开实现细节。

It's relatively frequent, but it's bad style.

If you have a look at the STL, for example, you'll remark that the class that yields const& or & to internal state are usually plain containers and only provide this kind of access for what you actually store in them. You have, obviously, no way to modify directly attributes such as size or the internal nodes of a binary tree.

Providing direct access to elements breaks encapsulation. Not only do you lose all class invariants wrt to those elements, but you also make them part of your API, and therefore cannot change the implementation of the class without also updating all clients.

As far as I am concerned, they are two types of classes in C++:

struct BigBag {
  Foo _foo;
  Bar _bar;
  FooBar _foobar;
};

class MyClass {
public:
  explicit MyClass(int a, int b);

  int getSomeInfo();

  void updateDetails(int a, int b);
private:
  // no peeking
};

That is: either it's just a collection of related elements, in which case everything is public and you're done, or there are class invariants and/or you're concerned with its API because there's a lot of client code, in which case you do not expose at all the implementation details.

戒ㄋ 2024-10-27 20:18:07

以防万一你想去掉括号……

class Cls {
    private:
        int _attr;
    public:
        Cls() : attr(_attr) { }
        int& attr;
};

编辑:克里斯的观点很好。用公共引用包装私有变量基本上没有任何好处。不过,以下版本确实添加了一些内容。将引用设置为 const 可防止设置,但允许获取私有变量。

class Cls {
    private:
        int _attr;
    public:
        Cls() : attr(_attr) { }
        int const & attr;
};

Just in case you want to get rid of the parentheses...

class Cls {
    private:
        int _attr;
    public:
        Cls() : attr(_attr) { }
        int& attr;
};

Edit: Chris' point is a good one. Essentially nothing is gained from wrapping a private variable with a public reference. The following version does add something though. Making the reference const prevents setting but allows getting of the private variable.

class Cls {
    private:
        int _attr;
    public:
        Cls() : attr(_attr) { }
        int const & attr;
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文