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

Any*_*orn 11 c++ java conventions

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

但是我得到了C++社区对这种做法不满的印象.如果是这样,是否有一个特殊的原因(除了更多的代码行)为什么会这样?

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

谢谢

Chu*_*dad 14

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

因此,虽然没有技术限制和(实际上很多)'set'和'get'方法,但我会说如果你想要太多的'get'和'set',你应该暂停并重新检查你的设计.您的类接口被系统中的太多其他实体使用.

  • 对于C++和Java(以及任何其他编程语言)都是如此.不要盲目地为你班级的每个领域添加getter和setter.只有在它们存在的原因时才添加它们. (4认同)
  • 操作是关键词.获取/设置对表示您不再对数据执行操作.它就是这样. (3认同)

And*_*son 10

在某些情况下,getter/setter是合适的,但是大量的getter/setter通常表明你的设计无法实现任何更高级别的抽象.

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

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

  • 标准库不使用它.
  • Bjarne Stroustrup 表示不喜欢它(最后一段):

    我特别不喜欢有很多get和set函数的类.这通常表明它本来不应该是一个阶级.它只是一个数据结构.如果它确实是一个数据结构,那就把它变成一个数据结构.


Pet*_*ter 8

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

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

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

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


Ski*_*izz 7

我认为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;
};
Run Code Online (Sandbox Code Playgroud)

然后可以像这样使用:

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;
}
Run Code Online (Sandbox Code Playgroud)

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

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;
  }
};
Run Code Online (Sandbox Code Playgroud)

它可以像这样使用:

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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