C++ 中的 set/get 方法

发布于 2024-09-17 16:33:40 字数 152 浏览 2 评论 0原文

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

然而我的印象是 C++ 社区不赞成这种做法。 如果是这样,是否有特殊原因(除了更多行代码之外)为什么会这样?

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

谢谢

Java programmers and API seems to favor explicit set/get methods.

however I got the impression C++ community frowns upon such practice.
If it is so,is there a particular reason (besides more lines of code) why this is so?

on the other hand, why does Java community choose to use methods rather than direct access?

Thank you

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

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

发布评论

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

评论(13

诗酒趁年少 2024-09-24 16:33:40

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

因此,虽然没有技术限制并且(实际上有大量)“设置”和“获取”方法,但我想说,如果您想要太多“获取”和“设置”,您应该暂停并重新检查您的设计。您的类接口被系统中太多其他实体使用。

A well designed class should ideally not have too many gets and sets. In my opinion, too many gets and sets are basically an indication of the fact that someone else (and potentially many of them) need my data to achieve their purpose. In that case, why does that data belong to me in the first place? This violates the basic principle of encapsulation (data + operations in one logical unit).

So, while there is no technical restriction and (in fact abundance of) 'set' and 'get' methods, I would say that you should pause and reinspect your design if you want too many of those 'get' and 'set' in your class interface used by too many other entities in your system.

云淡风轻 2024-09-24 16:33:40

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

通常,最好(就封装而言)为对象展示更高级别的操作,这不会使实现对用户来说显而易见。

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

  • 标准库不使用它。
  • Bjarne Stroustrup 表达他对此的不喜欢(最后一段):

    <块引用>

    我特别不喜欢有以下内容的课程
    很多获取和设置函数。那是
    通常表明不应该这样做
    首先已经是一个类了。
    它只是一个数据结构。如果它
    实际上是一个数据结构,使其成为
    数据结构。

There are occasions when getters/setters are appropriate but an abundance of getters/setters typically indicate that your design fails to achieve any higher level of abstraction.

Typically it's better (in regards to encapsulation) to exhibit higher level operations for your objects that does not make the implementation obvious to the user.

Some other possible reasons why it's not as common in C++ as in Java:

  • The Standard Library does not use it.
  • Bjarne Stroustrup expresses his dislike towards it (last paragraph):

    I particularly dislike classes with a
    lot of get and set functions. That is
    often an indication that it shouldn't
    have been a class in the first place.
    It's just a data structure. And if it
    really is a data structure, make it a
    data structure.

魂ガ小子 2024-09-24 16:33:40

我认为 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 接口。

另一个示例是更改谓词,以便它可以更改设置器值。在上面的 RGBValue 中,这可用于将值限制在 0 到 255 的范围内,而不是生成错误。

I think the reason the C++ community frowns on getters and setters is that C++ offers far better alternatives. For example:

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

which can then be used like this:

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

Notice that I added a predicate parameter to the property class. With this, we can get creative, for example, a property to hold an integer colour channel value:

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

and it can be used like this:

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

Notice that I made the handling of bad values a template parameter as well.

Using this as a starting point, it can be extended in many different ways.

For example, allow the storage of the property to be different to the public type of the value - so the RGBValue above could use an unsigned char for storage but an int interface.

Another example is to change the predicate so that it can alter the setter value. In the RGBValue above this could be used to clamp values to the range 0 to 255 rather than generate an error.

幸福丶如此 2024-09-24 16:33:40

反对 get/set 方法的常见论点是,如果您同时拥有这两种方法,并且它们只是微不足道的 return x;x = y; 那么您实际上还没有封装任何内容根本没有;您也可以将成员公开,这样可以节省大量的样板代码。

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

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

我不知道 C++ 社区对它们的不满程度比 Java 社区多或少。我的印象是,它们在 Python 等语言中相当不常见。

The usual argument against get/set methods is that if you have both and they're just trivial return x; and x = y; then you haven't actually encapsulated anything at all; you may as well just make the member public which saves a whole lot of boilerplate code.

Obviously there are cases where they still make sense; if you need to do something special in them, or you need to use inheritance or, particularly, interfaces.

There is the advantage that if you implement getters/setters you can change their implementation later without having to alter code that uses them. I suppose the frowning on it you refer to is kind of a YAGNI thing that if there's no expectation of ever altering the functions that way, then there's little benefit to having them. In many cases you can just deal with the case of altering the implementation later anyway.

I wasn't aware that the C++ community frowned on them any more or less than the Java community; my impression is that they're rather less common in languages like Python, for example.

墨离汐 2024-09-24 16:33:40

属性作为通用语言概念在技术上早于 C++,例如 Smalltalk,但它们从来都不是标准的一部分。 getter 和 setter 是 C++ 中用于开发 UI 时使用的一个概念,但说实话,使用有效的系统语言来开发 UI 是一个昂贵的提议。 C++ 中 getter 和 setter 的普遍问题是,由于它们不是标准,所以每个人都有不同的标准。

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

对于这个问题,您可能会得到很多观点,但总的来说,C++ 本来就是处理对象的 C,这使得不了解对象的开发人员也可以使用 OOP。将虚拟和模板引入该语言已经够困难的了,我认为它已经停滞了一段时间了。

Java 的不同之处在于,一开始,随着 Java 在垃圾收集等领域的引入,更容易推广健壮封装的理念,即外部实体应该让它们肮脏的小爪子远离类的内部元素。

我承认这只是一个观点——此时我使用 C++ 来实现高度优化的东西,比如 3D 图形管道——我已经必须管理所有的对象内存,所以我对那些根本上无用的代码持悲观态度,这些代码只是用于包装附加功能中的存储访问 - 也就是说,像 MSFT .net ILM 这样的运行时的基本性能能力使得这个位置有时很难捍卫

纯粹是我的 2c

Properties as a general language concept technically predate C++, e.g. in Smalltalk, but they weren't ever part of the standard. Getters and setters were a concept used in C++ when it was used for development of UI's, but truth be told, it's an expensive proposition to develop UI's in what is effectively a systems language. The general problem with getters and setters in C++ was that, since they weren't a standard, everybody had a different standard.

And in systems languages, where efficiency concerns are high, then it's just easier to make the variable itself public, although there's a lot of literature that frowns mightily on that practice. Often, you simply see richer exchanges of information between C++ object instances than simple items.

You'll probably get a lot of viewpoints in response to this question, but in general, C++ was meant to be C that did objects, making OOP accessable to developers that didn't know objects. It was hard enough to get virtuals and templates into the language, and I think that it's been kind of stagnant for a while.

Java differs because in the beginning, with what Java brought in areas like garbage collection, it was easier to promote the philosophy of robust encapsulation, i.e. external entities should keep their grubby little paws off of internal elements of a class.

I admit this is pretty much opinion - at this time I use C++ for highly optimized stuff like 3D graphics pipelines - I already have to manage all my object memory, so I'd take a dim view of fundamentally useless code that just serves to wrap storage access up in additional functions - that said, the basic performance capabilies of runtimes like the MSFT .net ILM make that a position that can be difficult to defend at times

Purely my 2c

乖乖兔^ω^ 2024-09-24 16:33:40

在 C++ 中具有显式 set/get 方法并没有什么不寻常的。我在大量的 C++ 中看到过它,不允许直接访问数据成员是非常有用的。

There's nothing unusual about having explicit set/get methods in C++. I've seen it in plenty of C++, it can be very useful to not allow direct access to data members.

以为你会在 2024-09-24 16:33:40

查看这个问题以获取解释为什么Java倾向于偏爱它们和C++的原因是一样的。简而言之:它允许您更改数据成员的访问方式,而无需强制客户端代码(使用您的代码的代码)重新编译。它还允许您针对如何访问数据以及访问该数据时执行的操作强制实施特定策略。

Check out this question for an explanation of why Java tends to prefer them and the reasons for C++ are the same. In short: it allows you to change the way data members are accessed without forcing client code (code that uses your code) to recompile. It also allows you to enforce a specific policy for how to access data and what to do when that data is accessed.

提笔书几行 2024-09-24 16:33:40

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

By mandating the use of set/get methods, one can implement useful side-effects in the getter/setter (for example, when the argument to get/set is an object).

骄兵必败 2024-09-24 16:33:40

我很惊讶还没有人提到 Java 自省和 beans。

使用 get.../set... 命名约定与内省相结合,允许对实用程序类进行各种巧妙的欺骗。

我个人认为“public”关键字应该足以触发豆子魔法,但我不是雷·高斯林。

我的看法是,在 C++ 中这是一个毫无意义的练习。您添加了至少六行代码来测试和维护,这些代码没有任何作用,并且大部分会被编译器忽略。除非您添加更多编码,否则它并不能真正保护您的类免受误用和滥用。

I am surprised nobody has mentioned Java introspection and beans yet.

Using get.../set... naming convention combined with introspection allows all sorts of clever trickery with utility classes.

I personally feel that the "public" keyword should have been enough to trigger the bean magic but I am not Ray Gosling.

My take on this is that in C++ is a rather pointless exercise. You are adding at least six lines of code to test and maintain which perform no purpose and will for the most part be ignored by the compiler. It doesnt really protect your class from misuse and abuse unless you add a lot more coding.

稀香 2024-09-24 16:33:40

我认为 C++ 社区并不反对使用 getter 和 setter。它们几乎总是一个好主意。

I don't think the C++ community frowned on using getters and setters. They are almost always a good idea.

〗斷ホ乔殘χμё〖 2024-09-24 16:33:40

它与面向对象编程的基础知识有关——向用户隐藏对象的内部结构。对象的用户不需要了解(也不应该关心)对象的内部结构。

它还使您可以控制每当对象的用户尝试读取/写入对象时要执行的操作。实际上,您向对象的用户公开了一个接口。他们必须使用该接口,并且您可以控制调用该接口中的方法时发生的情况 - getter 和 setter 将成为接口的一部分。

它只是让调试时的事情变得更容易。一个典型的场景是,当您的对象处于奇怪的状态时,您正在调试以找出它是如何到达那里的。您所做的就是在 getter 和 setter 中设置断点,并假设其他一切都很好,您就可以看到对象如何进入奇怪的状态。如果对象的用户都直接访问其成员,则弄清楚对象的状态何时发生变化变得更加困难(尽管并非不可能)

It has to do with the basics of object oriented programming - hiding the internals of an object from its users. The users of an object should not need to know (nor should they care) about the internals of an object.

It also gives you control over what is done whenever a user of your object tries to read/write to it. In effect, you expose an interface to the object's users. They have to use that interface and you control what happens when methods in that interface are called - the getters and setters would be part of the interface.

It just makes things easier when debugging. A typical scenario is when your object lands up in a weird state and you're debugging to find out how it got there. All you do is set breakpoints in your getters and setters and assuming all else is fine, you're able to see how your object gets to the weird state. If your object's users are all directly accessing its members, figuring out when your object's state changes becomes a lot harder (though not impossible)

找个人就嫁了吧 2024-09-24 16:33:40

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

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

在 C++ 中,这并不那么容易。该语言太复杂,IDE 根本无法可靠地做到这一点。

所以在 C++ 中,你最好第一次就做对。在Java中,你可以更具冒险精神。

I would argue that C++ needs getters/setters more than Java.

In Java, if you start with naked field access, and later you changed your mind, you want getter/setter instead, it is extremely easy to find all the usages of the field, and refactor them into getter/setter.

in C++, this is not that easy. The language is too complex, IDEs simply can't reliably do that.

so In C++, you better get it right the first time. In Java, you can be more adventurous.

南笙 2024-09-24 16:33:40

早在java之前就有了gets/sets。使用它们的原因有很多,特别是当您必须重新计算某事时。当值发生变化时。因此,第一个大优势是,您可以观察价值变化。但恕我直言,总是实现 get 和 set 是不好的——通常一个 get 就足够了。还有一点是,类别的变化会直接影响你的客户。如果不强制使用公共成员重构客户端代码,则无法更改成员名称。比方说,您有一个具有长度的对象,并且您更改了该成员名称......呃。有了吸气剂,你只需改变你这边的代码,客户端就可以睡个好觉了。为应该隐藏的成员添加gets/Sets当然是无稽之谈。

There were gets/sets long before java. There are many reasons to use them, especially, if you have to recalculate sth. wenn a value changes. So the first big advantage is, that you can watch to value changes. But imho its bad to ALWAYS implement get and set-often a get is enough. Another point is, that class changes will directly affect your customers. You cant change member names without forcing to refactor the clients code with public members. Lets say, you have an object with a lenght and you change this member name...uh. With a getter, you just change you side of the code and the client can sleep well. Adding gets/Sets for members that should be hidden is of course nonsense.

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