C++ 中访问器方法(getter 和 setter)的约定;

发布于 2024-09-17 11:39:32 字数 785 浏览 7 评论 0原文

关于 C++ 中的访问器方法的几个问题已经在 SO 上被问到了,但没有一个能够满足我对此问题的好奇心。

我尽可能避免访问器,因为像 Stroustrup 和其他著名程序员一样,我认为一个类中有很多访问器是糟糕的 OO 的标志。在 C++ 中,在大多数情况下,我可以向类添加更多责任或使用friend 关键字来避免它们。但在某些情况下,您确实需要访问特定的类成员。

有几种可能性:

1。根本不使用访问器

我们可以将相应的成员变量公开。这在 Java 中是不允许的,但在 C++ 社区中似乎没问题。但是,我有点担心应该返回对象的显式副本或只读(const)引用的情况,这是否夸张?

2.使用 Java 风格的 get/set 方法

我不确定它是否来自 Java,但我的意思是:

int getAmount(); // Returns the amount
void setAmount(int amount); // Sets the amount

3.使用目标 C 风格的 get/set 方法

这有点奇怪,但显然越来越常见:

int amount(); // Returns the amount
void amount(int amount); // Sets the amount

为了使其工作,您必须为您的成员变量找到一个不同的名称。有些人附加下划线,其他人则附加“m_”。我也不喜欢。

您使用哪种风格?为什么?

Several questions about accessor methods in C++ have been asked on SO, but none was able satisfy my curiosity on the issue.

I try to avoid accessors whenever possible, because, like Stroustrup and other famous programmers, I consider a class with many of them a sign of bad OO. In C++, I can in most cases add more responsibility to a class or use the friend keyword to avoid them. Yet in some cases, you really need access to specific class members.

There are several possibilities:

1. Don't use accessors at all

We can just make the respective member variables public. This is a no-go in Java, but seems to be OK with the C++ community. However, I'm a bit worried about cases were an explicit copy or a read-only (const) reference to an object should be returned, is that exaggerated?

2. Use Java-style get/set methods

I'm not sure if it's from Java at all, but I mean this:

int getAmount(); // Returns the amount
void setAmount(int amount); // Sets the amount

3. Use objective C-style get/set methods

This is a bit weird, but apparently increasingly common:

int amount(); // Returns the amount
void amount(int amount); // Sets the amount

In order for that to work, you will have to find a different name for your member variable. Some people append an underscore, others prepend "m_". I don't like either.

Which style do you use and why?

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

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

发布评论

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

评论(10

歌枕肩 2024-09-24 11:39:33

让我告诉你另一种可能性,这似乎是最简洁的。

需要阅读&修改

只需声明该变量 public:

class Worker {
public:
    int wage = 5000;
}

worker.wage = 8000;
cout << worker.wage << endl;

只需要 read

class Worker {
    int _wage = 5000;
public:
    inline int wage() {
        return _wage;
    }
}

worker.wage = 8000; // error !!
cout << worker.wage() << endl;

这种方法的缺点是,当您想要更改访问模式时,您需要更改所有调用代码(即添加括号)。

Let me tell you about one additional possiblity, which seems the most conscise.

Need to read & modify

Simply declare that variable public:

class Worker {
public:
    int wage = 5000;
}

worker.wage = 8000;
cout << worker.wage << endl;

Need just to read

class Worker {
    int _wage = 5000;
public:
    inline int wage() {
        return _wage;
    }
}

worker.wage = 8000; // error !!
cout << worker.wage() << endl;

The downside of this approach is that you need to change all the calling code (add parentheses, that is) when you want to change the access pattern.

旧时模样 2024-09-24 11:39:33

#3 的变体,我被告知这可能是“流畅”的风格

class foo {
  private: int bar;
  private: int narf;
  public: foo & bar(int);
  public: int bar();
  public: foo & narf(int);
  public: int narf();
};

//multi set (get is as expected)
foo f; f.bar(2).narf(3);

variation on #3, i'm told this could be 'fluent' style

class foo {
  private: int bar;
  private: int narf;
  public: foo & bar(int);
  public: int bar();
  public: foo & narf(int);
  public: int narf();
};

//multi set (get is as expected)
foo f; f.bar(2).narf(3);
芸娘子的小脾气 2024-09-24 11:39:32

从我的角度来看,从维护的角度来看,拥有 400 万行 C++ 代码(这只是一个项目),我会说:

  • 如果成员是不可变的(即 const) 或没有依赖关系的简单(如具有成员 X 和 Y 的点类)。

  • 如果成员是私有,那么跳过 getters/setters 也是可以的。如果 . cpp 单元很小。

  • 如果成员是publicprotectedprotectedpublic一样糟糕code>) 和非 const、非简单或具有依赖项,则使用 getter/setter。

作为一名维护人员,我想要拥有 getter/setter 的主要原因是因为这样我就有地方放置断点/日志记录/其他东西。

我更喜欢替代方案 2. 的风格,因为它更易于搜索(编写可维护代码的关键组成部分)。

From my perspective as sitting with 4 million lines of C++ code (and that's just one project) from a maintenance perspective I would say:

  • It's ok to not use getters/setters if members are immutable (i.e. const) or simple with no dependencies (like a point class with members X and Y).

  • If member is private only it's also ok to skip getters/setters. I also count members of internal pimpl-classes as private if the .cpp unit is smallish.

  • If member is public or protected (protected is just as bad as public) and non-const, non-simple or has dependencies then use getters/setters.

As a maintenance guy my main reason for wanting to have getters/setters is because then I have a place to put break points / logging / something else.

I prefer the style of alternative 2. as that's more searchable (a key component in writing maintainable code).

瑾夏年华 2024-09-24 11:39:32

2)是最好的IMO,因为它让你的意图最清晰。 set_amount(10)amount(10) 更有意义,并且一个很好的副作用是允许名为 amount 的成员。

公共变量通常是一个坏主意,因为没有封装。假设您需要在更新变量时更新缓存或刷新窗口?如果你的变量是公共的那就太糟糕了。如果您有设置方法,则可以将其添加到此处。

2) is the best IMO, because it makes your intentions clearest. set_amount(10) is more meaningful than amount(10), and as a nice side effect allows a member named amount.

Public variables is usually a bad idea, because there's no encapsulation. Suppose you need to update a cache or refresh a window when a variable is updated? Too bad if your variables are public. If you have a set method, you can add it there.

划一舟意中人 2024-09-24 11:39:32
  1. 我从不使用这种风格。因为它会限制类设计的未来,并且显式的 getter 或 setter 与良好的编译器一样高效。

    当然,实际上,内联显式 getter 或 setter 会创建对类实现同样多的底层依赖。它们只是减少语义依赖性。如果更改它们,您仍然必须重新编译所有内容。

  2. 这是我使用访问器方法时的默认样式。

  3. 这种风格对我来说似乎太“聪明”了。我确实在极少数情况下使用它,但仅在我真正希望访问器尽可能感觉像变量的情况下。

我确实认为有一个简单的变量袋可能带有构造函数的情况,以确保它们都被初始化为正常的东西。当我这样做时,我只需将其设为一个struct 并将其全部公开。

  1. I never use this style. Because it can limit the future of your class design and explicit geters or setters are just as efficient with a good compilers.

    Of course, in reality inline explicit getters or setters create just as much underlying dependency on the class implementation. THey just reduce semantic dependency. You still have to recompile everything if you change them.

  2. This is my default style when I use accessor methods.

  3. This style seems too 'clever' to me. I do use it on rare occasions, but only in cases where I really want the accessor to feel as much as possible like a variable.

I do think there is a case for simple bags of variables with possibly a constructor to make sure they're all initialized to something sane. When I do this, I simply make it a struct and leave it all public.

ぇ气 2024-09-24 11:39:32
  1. 如果我们只想表示数据,这是一个很好的风格。

  2. 我不喜欢它:)因为当我们可以在C++中重载它们时,get_/set_实际上是不必要的。

  3. STL 使用这种样式,例如 std::streamString::strstd::ios_base::flags,除非应该避免!什么时候?当方法的名称与其他类型的名称冲突时,则使用 get_/set_ 样式,例如 std::string::get_allocator 因为 std::allocator< /code>.

  1. That is a good style if we just want to represent pure data.

  2. I don't like it :) because get_/set_ is really unnecessary when we can overload them in C++.

  3. STL uses this style, such as std::streamString::str and std::ios_base::flags, except when it should be avoided! when? When method's name conflicts with other type's name, then get_/set_ style is used, such as std::string::get_allocator because of std::allocator.

北座城市 2024-09-24 11:39:32

总的来说,我觉得系统中太多的实体使用太多的 getter 和 setter 并不是一个好主意。这仅表明设计不良或封装错误。

话虽如此,如果这样的设计需要重构,并且源代码可用,我更愿意使用访问者设计模式。原因是:

a.它让班级有机会
决定允许谁访问其
私人国家

b.它给了一个类一个
有机会决定访问什么
允许每个实体
对其私有状态感兴趣

c.它
清楚地记录此类外部访问
通过清晰的类接口

基本思想是:

a) 如果可能的话重新设计,

b)
重构使得

  1. 对类状态的所有访问都是通过众所周知的个人主义
    界面

  2. 应该可以配置某种该做和不该做的事情
    到每个这样的接口,例如所有
    来自外部实体的访问
    应该允许,所有访问来自
    外部实体BAD应该是
    不允许,外部实体确定
    应该允许获取但不允许设置(例如)

In general, I feel that it is not a good idea to have too many getters and setters being used by too many entities in the system. It is just an indication of a bad design or wrong encapsulation.

Having said that, if such a design needs to be refactored, and the source code is available, I would prefer to use the Visitor Design pattern. The reason is:

a. It gives a class an opportunity to
decide whom to allow access to its
private state

b. It gives a class an
opportunity to decide what access to
allow to each of the entities who are
interested in its private state

c. It
clearly documents such exteral access
via a clear class interface

Basic idea is:

a) Redesign if possible else,

b)
Refactor such that

  1. All access to class state is via a well known individualistic
    interface

  2. It should be possible to configure some kind of do's and don'ts
    to each such interface, e.g. all
    access from external entity GOOD
    should be allowed, all access from
    external entity BAD should be
    disallowed, and external entity OK
    should be allowed to get but not set (for example)

绿光 2024-09-24 11:39:32
  1. 我不会排除访问器的使用。对于某些 POD 结构可能是这样,但我认为它们是一件好事(某些访问器也可能有额外的逻辑)。

  2. 如果代码保持一致,命名约定并不重要。如果您使用多个第三方库,它们可能会使用不同的命名约定。所以这是一个品味问题。

  1. I would not exclude accessors from use. May for some POD structures, but I consider them a good thing (some accessors might have additional logic, too).

  2. It doesn't realy matters the naming convention, if you are consistent in your code. If you are using several third party libraries, they might use different naming conventions anyway. So it is a matter of taste.

影子是时光的心 2024-09-24 11:39:32

我已经看到类的理想化而不是整数类型来引用有意义的数据。

像下面这样的东西通常没有很好地利用 C++ 属性:

struct particle {
    float mass;
    float acceleration;
    float velocity;
} p;

为什么?因为 p.mass*p.acceleration 的结果是浮点数,而不是预期的力。

指定用途的类定义(即使它是一个值,如前面提到的 amount)更有意义,并允许我们执行以下操作:

struct amount
{
    int value;

    amount() : value( 0 ) {}
    amount( int value0 ) : value( value0 ) {}
    operator int()& { return value; }
    operator int()const& { return value; }
    amount& operator = ( int const newvalue )
    {
        value = newvalue;
        return *this;
    }
};

您可以通过运算符隐式访问 amount 中的值国际。此外:

struct wage
{
    amount balance;

    operator amount()& { return balance; }
    operator amount()const& { return balance; }
    wage& operator = ( amount const&  newbalance )
    {
        balance = newbalance;
        return *this;
    }
};

Getter/Setter 用法:

void wage_test()
{
    wage worker;
    (amount&)worker = 100; // if you like this, can remove = operator
    worker = amount(105);  // an alternative if the first one is too weird
    int value = (amount)worker; // getting amount is more clear
}

这是一种不同的方法,并不意味着它是好还是坏,而是不同。

I've seen the idealization of classes instead of integral types to refer to meaningful data.

Something like this below is generally not making good use of C++ properties:

struct particle {
    float mass;
    float acceleration;
    float velocity;
} p;

Why? Because the result of p.mass*p.acceleration is a float and not force as expected.

The definition of classes to designate a purpose (even if it's a value, like amount mentioned earlier) makes more sense, and allow us to do something like:

struct amount
{
    int value;

    amount() : value( 0 ) {}
    amount( int value0 ) : value( value0 ) {}
    operator int()& { return value; }
    operator int()const& { return value; }
    amount& operator = ( int const newvalue )
    {
        value = newvalue;
        return *this;
    }
};

You can access the value in amount implicitly by the operator int. Furthermore:

struct wage
{
    amount balance;

    operator amount()& { return balance; }
    operator amount()const& { return balance; }
    wage& operator = ( amount const&  newbalance )
    {
        balance = newbalance;
        return *this;
    }
};

Getter/Setter usage:

void wage_test()
{
    wage worker;
    (amount&)worker = 100; // if you like this, can remove = operator
    worker = amount(105);  // an alternative if the first one is too weird
    int value = (amount)worker; // getting amount is more clear
}

This is a different approach, doesn't mean it's good or bad, but different.

滥情空心 2024-09-24 11:39:32

另一种可能性是:

int& amount();

我不确定我是否会推荐它,但它的优点是不寻常的符号可以阻止用户修改数据。

str.length() = 5; // Ok string is a very bad example :)

有时这可能是一个不错的选择:

image(point) = 255;  

另一种可能性是,使用函数符号来修改对象。

edit::change_amount(obj, val)

这样,危险/编辑功能就可以通过它自己的文档放在一个单独的命名空间中。这似乎是泛型编程中自然而然的事情。

An additional possibility could be :

int& amount();

I'm not sure I would recommend it, but it has the advantage that the unusual notation can refrain users to modify data.

str.length() = 5; // Ok string is a very bad example :)

Sometimes it is maybe just the good choice to make:

image(point) = 255;  

Another possibility again, use functional notation to modify the object.

edit::change_amount(obj, val)

This way dangerous/editing function can be pulled away in a separate namespace with it's own documentation. This one seems to come naturally with generic programming.

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