为什么std :: pair公开成员variables?

从http://www.cplusplus.com/reference/utility/pair/ ,我们知道std::pair有两个成员variables, firstsecond

为什么STLdevise者决定暴露两个成员variables, firstsecond ,而不是提供getFirst()getSecond()

对于原始的C ++ 03 std::pair ,访问成员的函数没有任何用处。

从C ++ 11和更高版本(我们现在在C ++ 14,C ++ 17快速上来) std::pairstd::tuple一个特殊情况,其中std::tuple可以有任何东西的个数。 因此,有一个参数化的getter是有意义的,因为发明和标准化任意数量的项目名称是不切实际的。 因此,你也可以使用std::get作为std::pair

所以,devise的原因是历史性的,当前的std::pair是向更普遍性演进的最终结果。


其他消息:

关于

据我所知,如果封装上面的两个成员variables并给出getFirst();getSecond()

不,这是垃圾。

这就像说锤子总是比较好,无论是用钉子打钉,用螺丝钉固定,还是修剪一块木头。 特别是在最后一种情况下,锤子不是一个有用的工具。 锤子可以是非常有用的,但这并不意味着他们总体上“更好”,这只是无稽之谈。

如果有人认为获取或设置值需要额外的逻辑(改变某些内部状态),那么获取者和设置者通常是有用的。 这可以很容易地添加到方法中。 在这种情况下, std::pair仅用于提供2个数据值。 没有什么比这更less的了。 因此,增加一个getter和setter的冗长是毫无意义的。

原因是数据结构不需要强加实际的variables,因为std::pair为两个元素build立了一个通用的容器。 换句话说,typesstd::pair<T, U>被假定为分别对于TUtypes的任何可能的firstsecond元素都是有效的。 同样,其元素价值的后续变化不能真正影响std::pair本身的有效性。

Alex Stepanov(STL的作者)在评论singleton容器(即一个元素的容器)时,在他的“ 用元件高效编程”课程中明确地提出了这个一般devise原则。

因此,尽pipe这个原则本身可以成为争论的来源,但这也是std::pairforms背后的原因。

如果人们认为抽象是保证用户不受deviseselect的限制,并且现在或将来在这些select上做出改变,那么吸气和吸气器就很有用。

“now”的典型示例是setter / getter可能有逻辑来validation和/或计算值 – 例如,使用setter作为电话号码,而不是直接暴露字段,以便检查格式; 为集合使用getter,以便getter可以向调用者提供成员值(集合)的只读视图。

“未来变化”的规范( 虽然不好 )的例子是Point – 你应该公开xygetX()getY()吗? 通常的答案是使用getters / setter,因为在将来的某个时候,您可能希望将内部表示从笛卡儿更改为polar,并且不希望用户受到影响(或让他们依赖于该devise决策) 。

std::pair的情况下,这个类现在和永远直接表示两个值,并且直接表示任意types的两个值,并按需提供它们的值。 而已。 这就是为什么devise使用直接成员访问,而不是通过getter / setter。

有人可能会认为, std::pair会更好的访问者函数访问其成员! 值得注意的是,退化std::pair情况可能是一个优势。 例如,当至less有一个types是空的非最终类时,这些对象可能会变小(空的基地可能会成为一个基地,不需要获得自己的地址)。

std::pair被发明的时候,这些特殊的情况并没有被考虑(我不确定在当时的工作文件中是否允许空基优化)。 从语义上讲,访问函数没有多less理由:显然,访问者需要为非常量对象返回一个可变引用。 因此访问者不提供任何forms的封装。

另一方面,优化器使得访问函数被使用时发生了什么变得更加困难,例如因为引入了附加的顺序点。 我可以想象,孟利和亚历山大·斯捷潘诺夫甚至衡量是否有区别(我也没有)。 即使他们不这样做,直接提供对成员的访问肯定不会比访问函数慢,反之不一定是正确的。

我不是这个决定的一部分,C ++标准没有理由,但我这是一个故意的决定,使成员的公共数据成员。

获取者和设置者的主要目的是获取对访问的控制权。 也就是说,如果将“first”作为variables公开,任何类都可以读写(如果不是const ),而不告诉它是哪个类的一部分。 在许多情况下,这可能会造成严重的问题。

例如,假设你有一个class级代表乘船人数。 您将乘客人数存储为整数。 如果您将该数字作为裸露variables显示,则外部函数可能会写入该variables。 如果真的有10名乘客的话,那么这个数字可能会让你变动,但有人把这个variables(可能是偶然的)改成了50。这是乘客人数的一个吸气的情况(但不是一个二传手, 问题 )。

getter和setter的一个例子是一个代表一个math向量的类,在这个向量中你想要caching关于向量的某些信息。 说你想存储的长度。 在这种情况下,更改vec.x可能会改变长度/大小。 所以,你不仅需要将x包装在一个getter中,还必须为x提供一个setter,它知道要更新vector的caching长度。 (当然,大多数实际的math库不会caching这些值,从而暴露variables。)

所以你应该在使用它们的上下文中问你自己的问题是:这个类是否曾经想要控制或者被告知这个variables的变化?

像std :: pair这样的答案是平坦的“不”。 没有任何控制访问会员的唯一目的是遏制这些会员的情况。 当然,这两个variables是否已经被触及,并不需要两人之间的联系,因为考虑到这些variables是其中唯一的两个成员,所以没有更新的状态。 对不知道它实际上包含了什么和它的含义,所以跟踪它包含的内容是不值得的。

取决于编译器及其configuration方式,getter和setter可能会引入开销。 这在大多数情况下可能并不重要,但是如果你把它们放在std::pair这样基本的东西上,这将是一个不重要的问题。 因此,他们的补充需要合理 – 正如我刚才解释的那样,这是不可能的。

对于面向对象devise没有基本理解的评论数量感到惊讶(这是否certificatec ++不是OO语言?)。 是的,std :: pair的devise有一些历史的特征,但是这不会使糟糕的devise变得更好。 也不能以此为借口否认事实。 在我咆哮之前,让我回答评论中的一些问题:

你不认为int也应该有一个setter和getter

是的,从devise的angular度来看,我们应该使用访问器,因为这样做我们什么都不会损失,而是获得额外的灵活性。 一些较新的algorithm可能想要在键/值中包含额外的位,并且不能在没有访问器的情况下对它们进行编码/解码。

如果getter中没有逻辑,为什么在getter中包含某些东西?

你怎么知道getter / setter中没有逻辑? 一个好的devise不应该限制基于猜测的实现的可能性。 它应该提供尽可能多的灵活性。 记住std:pair的devise也决定了迭代器的devise,并且要求用户直接访问成员variables,迭代器必须返回实际存储键/值的结构。 事实certificate这是一个很大的局限性。 有些algorithm需要将它们分开。 有些algorithm根本不存储键/值。 现在他们必须在迭代过程中复制数据。

与stream行的观点相反,拥有除了用getter和setter存储成员variables什么都不做的对象不是“应该做的事情”

另一个猜测。

好吧,我会停在这里。

为了回答最初的问题:std :: pairselect暴露成员variables,因为devise它的人不承认和/或优先考虑灵活合同的重要性。 对于map / hashtable中的key-value对应该如何实现,他们显然有一个非常狭隘的想法/愿景,而且更糟糕的是,他们让这样一个狭隘的实现视图溢于顶端,从而危及devise。 例如,如果我想实现std:unordered_map的replace,它将基于开放寻址scheme的键和值存储在单独的数组中,并使用线性探测? 这可以极大地提高小键和大值对的caching性能,因为您不需要跨越空间占用空间来探测键。 如果std :: pairselect访问器,为此编写一个STL样式的迭代器将是微不足道的。 但是现在,如果不引发额外的数据复制,就无法做到这一点。

我注意到,他们还要求使用开放散列(即闭链)来实现std :: unordered_map。 从devise的angular度来看,这不仅是奇怪的(为什么要限制如何实现?),而且在实现方面也相当愚蠢 – 使用链表的链式哈希表可能是所有类别中速度最慢的。 在谷歌网页,我们可以很容易地发现,std:unordered_map往往是一个哈希表基准的门垫。 它甚至比Java的HashMap慢(我不知道在这种情况下他们是如何落后的,因为HashMap也是一个链式散列表)。 一个古老的借口是,当load_factor接近1时,链式散列表往往会更好地执行,这是完全无效的,因为1)在开放寻址家族中有很多技术来处理这个问题 – 曾听说过跳房子或罗宾汉哈希,后者实际上在那里已经有三十年了, 2)链式散列表为每个条目添加一个指针(64位机器上的好8个字节)的开销,所以当我们说unordered_map的load_factor接近1时,它不是100%的内存使用! 我们应该考虑这一点,并将unordered_map的性能与具有相同内存使用情况的替代方法进行比较。 事实certificate,像Google Dense HashMap这样的select比std :: unordered_map快3-4倍。

为什么这些是相关的? 因为有趣的是,强制开放散列确实使得std :: pair的devise看起来不那么糟糕,现在我们不需要另一种存储结构的灵活性。 而且,std :: pair的存在使得几乎不可能采用更新/更好的algorithm来编写std :: unordered_map的embedded式replace。 有时候你想知道他们是否有意这样做,以便std :: pair的糟糕devise和std :: unordered_map的行人执行可以一起存活更长时间。 当然,我在开玩笑,所以无论谁写这些,都不要生气。 事实上,使用Java或Python的人(好吧,我承认Python有一段时间了)想要感谢你让自己“像C ++一样快”。