82

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

我尽可能避免使用访问器,因为像 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_”。我也不喜欢。

你使用哪种风格,为什么?

4

10 回答 10

45

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

  • 如果成员是不可变的(即const)或没有依赖关系的简单成员(例如具有成员 X 和 Y 的点类),则可以不使用 getter/setter。

  • 如果private只有成员,也可以跳过 getter/setter。我还计算内部 pimpl 类的成员,好像private.cpp 单元很小一样。

  • 如果 member is publicor protected( protectedis as bad as public) and non- const, non-simple or 具有依赖关系,则使用 getter/setter。

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

我更喜欢备选方案 2 的风格,因为它更易于搜索(编写可维护代码的关键组件)。

于 2010-09-06T04:51:59.850 回答
9

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

公共变量通常不是一个好主意,因为没有封装。假设您需要在更新变量时更新缓存或刷新窗口?如果您的变量是公开的,那就太糟糕了。如果你有一个 set 方法,你可以在那里添加它。

于 2010-09-05T19:58:36.540 回答
7
  1. 我从不使用这种风格。因为它会限制你的类设计的未来,而显式的 geter 或 setter 与好的编译器一样有效。

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

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

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

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

于 2010-09-05T19:39:09.053 回答
6
  1. 如果我们只想表示pure数据,那是一种很好的风格。

  2. 我不喜欢它:) 因为get_/set_当我们可以在 C++ 中重载它们时真的没有必要。

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

于 2010-09-06T01:49:56.480 回答
4

总的来说,我觉得让太多的 getter 和 setter 被系统中的太多实体使用并不是一个好主意。这只是一个糟糕的设计或错误封装的指示。

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

一种。它让一个类有机会决定允许谁访问它的私有状态

湾。它使一个类有机会决定允许对其私有状态感兴趣的每个实体进行哪些访问

C。它通过清晰的类接口清楚地记录了这种外部访问

基本思路是:

a) 如果可能,重新设计,否则,

b) 重构使得

  1. 所有对类状态的访问都是通过一个众所周知的个性化 接口

  2. 应该可以为每个这样的接口配置某种注意事项,例如 应该允许来自外部实体GOOD的所有访问,应该禁止来自外部实体BAD的所有访问,并且 应该允许外部实体OK但是未设置(例如)

于 2010-09-06T02:32:29.450 回答
2
  1. 我不会排除访问者的使用。可能适用于一些 POD 结构,但我认为它们是一件好事(一些访问器也可能有额外的逻辑)。

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

于 2010-09-05T19:34:36.613 回答
2

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

下面这样的事情通常不能很好地利用 C++ 属性:

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

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

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

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;
    }
};

您可以通过运算符 int 隐式访问数量值。此外:

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
}

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

于 2017-07-20T21:36:47.790 回答
1

另一种可能性是:

int& amount();

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

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

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

image(point) = 255;  

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

edit::change_amount(obj, val)

这样,危险/编辑功能可以在一个单独的命名空间中使用它自己的文档来提取。这似乎是泛型编程自然而然的。

于 2010-09-05T22:15:25.183 回答
0

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

需要阅读和修改

只需将该变量声明为 public:

class Worker {
public:
    int wage = 5000;
}

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

只需要阅读

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

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

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

于 2014-02-15T12:51:52.903 回答
0

#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);
于 2020-07-23T22:36:24.973 回答