在C ++中设置/获取方法

时间:2010-09-03 01:46:25

标签: java c++ conventions

Java程序员和API似乎更喜欢显式的set / get方法。

然而,我得到了C ++社区对这种做法不满的印象。 如果是这样,有没有特别的原因(除了更多的代码行)为什么会这样呢?

另一方面,为什么Java社区选择使用方法而不是直接访问?

谢谢

13 个答案:

答案 0 :(得分:14)

理想情况下,设计良好的类应该没有太多的获取和设置。在我看来,太多的获取和集合基本上表明其他人(可能还有许多人)需要我的数据来实现他们的目的。在那种情况下,为什么这些数据首先属于我?这违反了封装的基本原则(数据+操作在一个逻辑单元中)。

所以,虽然没有技术限制和(实际上很多)'set'和'get'方法,但我会说你应该暂停并重新考虑你的设计,如果你想要太多的'get'和'在系统中太多其他实体使用的类接口中设置'。

答案 1 :(得分:10)

有时候getter / setter是合适的,但是大量的getter / setter通常表明你的设计无法实现更高级别的抽象。

通常情况下(对于封装而言)更好地为您的对象展示更高级别的操作,这些操作不会使用户明白实现。

其他一些可能的原因导致它在C ++中不如Java中那么常见:

  • 标准库不使用它。
  • Bjarne Stroustrup expresses他不喜欢它(最后一段):
      

    我特别不喜欢有课程的课程   很多获取和设置功能。那是   经常表明它不应该   一直是一个班级。   它只是一个数据结构。如果它   真的是一个数据结构,让它成为一个   数据结构。

答案 2 :(得分:7)

反对get / set方法的常见理由是,如果你有两个并且它们只是微不足道return x;x = y;那么你实际上根本没有封装任何东西;您也可以将该成员公开,这样可以节省大量的样板代码。

显然有些情况下他们仍然有意义;如果你需要在它们中做一些特殊的事情,或者你需要使用继承,特别是接口。

优点是,如果您实现getter / setter,您可以在以后更改其实现,而无需更改使用它们的代码。我想你所提到的皱眉是一种YAGNI的东西,如果没有期望以这种方式改变功能,那么拥有它们几乎没有什么好处。在许多情况下,您无论如何都可以处理更改实现的情况。

我不知道C ++社区对Java社区不屑一顾;我的印象是,它们在Python等语言中并不常见。例如。

答案 3 :(得分:6)

我认为C ++社区对getter和setter不满的原因是C ++提供了更好的选择。例如:

template <class T>
class DefaultPredicate
{
public:
  static bool CheckSetter (T value)
  {
    return true;
  }
  static void CheckGetter (T value)
  {
  }
};

template <class T, class Predicate = DefaultPredicate <T>>
class Property
{
public:
  operator T ()
  {
    Predicate::CheckGetter (m_storage);
    return m_storage;
  }
  Property <T, Predicate> &operator = (T rhs)
  {
    if (Predicate::CheckSetter (rhs))
    {
      m_storage = rhs;
    }
    return *this;
  }
private:
  T m_storage;
};

然后可以像这样使用:

class Test
{
public:
  Property <int> TestData;
  Property <int> MoreTestData;
};

int main ()
{
  Test
    test;

  test.TestData = 42;
  test.MoreTestData = 24;
  int value = test.TestData;
  bool check = test.TestData == test.MoreTestData;
}

请注意,我在属性类中添加了一个谓词参数。有了这个,我们可以获得创造性,例如,一个属性来保存整数颜色通道值:

class NoErrorHandler
{
public:
  static void SignalError (const char *const error)
  {
  }
};

class LogError
{
public:
  static void SignalError (const char *const error)
  {
    std::cout << error << std::endl;
  }
};

class Exception
{
public:
  Exception (const char *const message) :
    m_message (message)
  {
  }

  operator const char *const ()
  {
    return m_message;
  }

private:
  const char
    *const m_message;
};

class ThrowError
{
public:
  static void SignalError (const char *const error)
  {
    throw new Exception (error);
  }
};

template <class ErrorHandler = NoErrorHandler>
class RGBValuePredicate : public DefaultPredicate <int>
{
public:
  static bool CheckSetter (int rhs)
  {
    bool
      setter_ok = true;

    if (rhs < 0 || rhs > 255)
    {
      ErrorHandler::SignalError ("RGB value out of range.");
      setter_ok = false;
    }

    return setter_ok;
  }
};

它可以像这样使用:

class Test
{
public:
  Property <int, RGBValuePredicate <> > RGBValue1;
  Property <int, RGBValuePredicate <LogError> > RGBValue2;
  Property <int, RGBValuePredicate <ThrowError> > RGBValue3;
};

int main ()
{
  Test
    test;

  try
  {
    test.RGBValue1 = 4;
    test.RGBValue2 = 5;
    test.RGBValue3 = 6;
    test.RGBValue1 = 400;
    test.RGBValue2 = 500;
    test.RGBValue3 = -6;
  }
  catch (Exception *error)
  {
    std::cout << "Exception: " << *error << std::endl;
  }
}

请注意,我也将错误值的处理作为模板参数。

以此为出发点,可以通过多种方式进行扩展。

例如,允许属性的存储与值的公共类型不同 - 因此上面的RGBValue可以使用unsigned char进行存储,但使用int接口。

另一个例子是更改谓词,以便它可以改变setter值。在上面的RGBValue中,这可用于将值钳位到0到25​​5的范围,而不是生成错误。

答案 4 :(得分:3)

作为通用语言概念的属性在技术上早于C ++,例如在Smalltalk中,但它们并不是标准的一部分。当用于开发UI时,Getters和setter是C ++中使用的概念,但事实上,在有效的系统语言中开发UI是一个昂贵的主张。 C ++中getter和setter的一般问题是,因为它们不是标准,所以每个人都有不同的标准。

在系统语言中,效率问题很高,然后将变量本身公开变得更容易,尽管有很多文献对这种做法非常苛刻。通常,您只是在C ++对象实例之间看到比简单项目更丰富的信息交换。

你可能会在回答这个问题时得到很多观点,但总的来说,C ++本来就是做C对象的对象,使得OOP可以访问不了解对象的开发人员。将虚拟和模板纳入语言是很困难的,我认为它暂时停滞不前。

Java的不同之处在于,一开始,Java带来了像垃圾收集这样的领域,它更容易推广强大的封装理念,即外部实体应该保持他们的肮脏的小爪子脱离类的内部元素。

我承认这是非常多的意见 - 此时我使用C ++进行高度优化的东西,比如3D图形管道 - 我已经必须管理所有的对象内存,所以我会对根本无用的代码进行简单的观察用于将存储访问包装在其他功能中 - 也就是说,像MSFT .net ILM这样的运行时的基本性能能力使得这个位置有时难以防御

纯粹是我的2c

答案 5 :(得分:2)

在C ++中使用显式set / get方法并不罕见。我已经在很多C ++中看到它,不允许直接访问数据成员非常有用。

答案 6 :(得分:1)

查看this question,了解Java倾向于选择它们的原因,以及C ++的原因是相同的。简而言之:它允许您更改访问数据成员的方式,而无需强制客户端代码(使用您的代码的代码)重新编译。它还允许您强制执行特定策略,以了解如何访问数据以及访问数据时要执行的操作。

答案 7 :(得分:1)

通过强制使用set / get方法,可以在getter / setter中实现有用的副作用(例如,当get / set的参数是一个对象时)。

答案 8 :(得分:1)

我很惊讶没有人提到Java内省和bean。

使用get ... / set ...命名约定与内省相结合,可以使用实用程序类进行各种巧妙的诡计。

我个人觉得“public”关键字应该足以触发bean魔法,但我不是Ray Gosling。

我对此的看法是,在C ++中是一种相当无意义的练习。您正在添加至少六行代码来进行测试和维护,这些代码没有任何用处,并且大部分都会被编译器忽略。除非您添加更多编码,否则它并不能真正保护您的班级免遭滥用和滥用。

答案 9 :(得分:0)

我认为C ++社区不赞成使用getter和setter。他们几乎总是一个好主意。

答案 10 :(得分:0)

它与面向对象编程的基础有关 - 将对象的内部隐藏在其用户之外。对象的用户不应该知道(也不应该关心)对象的内部。

它还使您可以控制对象的用户尝试读取/写入时所执行的操作。实际上,您将接口暴露给对象的用户。他们必须使用该接口,并控制在调用该接口中的方法时会发生什么 - getter和setter将成为接口的一部分。

调试时只是简单易行。一个典型的场景是当你的对象以一种奇怪的状态出现时,你正在调试以找出它是如何到达那里的。你要做的只是在你的getter和setter中设置断点,并假设其他一切都很好,你就能看到你的对象如何进入奇怪的状态。如果您的对象的用户都直接访问其成员,那么确定对象的状态更改会变得更加困难(尽管并非不可能)

答案 11 :(得分:0)

我认为C ++比Java更需要getter / setter。

在Java中,如果你从裸体访问开始,后来你改变主意,你想要getter / setter,那么很容易找到该字段的所有用法,并将它们重构为getter / setter。 / p>

在C ++中,这并不容易。语言过于复杂,IDE根本无法可靠地执行此操作。

所以在C ++中,你最好第一次就把它弄好。在Java中,您可以更具冒险精神。

答案 12 :(得分:0)

在java之前有很多获取/设置。有很多理由使用它们,特别是,如果你必须重新计算某事。值得改变。因此,第一大优势是,您可以关注价值变化。但是,我总是很难实现获取和设置 - 通常一个获得就足够了。另一点是,班级变化将直接影响您的客户。您无法更改成员名称,而无需使用公共成员重构客户端代码。可以说,你有一个长度的对象,你改变了这个成员名...呃。使用getter,您只需更改代码,客户端就可以安然入睡。为隐藏的成员添加gets / Sets当然是无稽之谈。