公共数据成员vs Getters,Setters

lia*_*iaK 70 c++ getter setter

我目前在Qt等C++工作.我正在拥有具有私有数据成员和公共成员函数的类.我有班级中可用的数据成员的公共getter和setter.

现在我的问题是,如果我们的类中有数据成员的getter和setter,那么将这些数据成员设置为私有的重点是什么?我同意基类中的私有数据成员听起来合乎逻辑.但除此之外,拥有私人会员以及他们的吸气者和制定者对我来说似乎并不合乎逻辑.

或者我们可以将所有变量都公开,以便根本不需要getter和setter吗?拥有那些是一个好习惯吗?我知道让私有成员确保数据抽象,但让getter和setter实际上可以很容易地访问这些变量.欢迎任何关于此的指示.

jmu*_*llo 72

都不是.你应该有办法做事.如果其中一个事件恰好与一个特定的内部变量相对应,那么这个内部变量很好,但是应该没有任何内容可以向你的班级用户发送电报.

私有数据是私有的,因此您可以随时替换实现(并且可以执行完全重建,但这是一个不同的问题).一旦你将精灵从瓶子里拿出来,你就会发现不可能把它推回去.

编辑:发表评论我做了另一个答案.

我的观点是你提出错误的问题.关于使用getter/setter或拥有公共成员,没有最佳实践.只有最适合你的特定对象的东西,以及它如何模拟某些特定的现实世界的东西(或者在游戏的情况下可能是想象的东西).

个人吸气者/背叛者是两个邪恶中的较小者.因为一旦你开始制作getter/setter,人们就会停止设计具有批判性眼光的对象,以确定哪些数据应该可见,哪些数据不应该.对于公众成员来说,情况甚至更糟,因为倾向于让一切都公开.

相反,检查对象的作用以及它对于某个对象的意义.然后创建为该对象提供自然界面的方法.它的自然界面涉及使用getter和setter来暴露一些内部属性,所以就这样吧.但重要的是你提前考虑了它并为设计合理的原因创造了吸气剂/制定者.

  • 另一个有用的区别:您可以轻松地在成员函数上放置调试断点,但是对于类的所有实例化,将调试断点放置在内存位置上要困难得多。这开始引起诸如以下的问题:我是否需要知道何时进行更改或访问?这些数据成员之间是否必须维护任何关系?多线程安全要求是什么? (3认同)

AnT*_*AnT 34

不,它甚至不是一回事.

通过不同的类接口方法可以实现不同级别的保护/实现隐藏:


1.公共数据成员:

  • 提供对数据成员的读取和写入(如果不是const)访问
  • 暴露数据对象物理存在并且物理上是该类的成员的事实(允许创建指向该成员的指针到成员类型的指针)
  • 提供对数据成员的左值访问(允许创建指向成员的普通指针)


2.一种返回对一段数据(可能是私有数据成员)的引用的方法:

  • 提供对数据的读和写(如果不是const)访问
  • 暴露了数据对象在物理上存在但不暴露它实际上是该类的成员的事实(不允许创建指向数据的指针到成员类型的指针)
  • 提供对数据的左值访问(允许人们创建指向它的普通指针)


3. Getter和/或setter方法(可能访问私有数据成员):

  • 提供对属性的读取和/或写入访问
  • 没有暴露数据对象物理存在的事实,更不用说物理存在于这个类中(不允许创建指向该数据的指针到成员类型的指针,或者任何类型的指针)
  • 不提供对数据的左值访问(不允许创建普通指针)

getter/setter方法甚至没有暴露属性由物理对象实现的事实.即getter/setter对后面可能没有物理数据成员.

将上面的内容记入帐户,看到有人声称getter和setter对与公共数据成员相同是很奇怪的.事实上,他们没有任何共同之处.

当然,每种方法都有不同的变化.例如,getter方法可能会返回对数据的const引用,这会将它放在(2)和(3)之间.

  • 它们可能不一样,但它们用于相同的目的 - 提供对象的属性的公共访问. (2认同)
  • 您正在从C++语言的角度来看这种形式 - 我的答案来自应用程序设计的角度. (2认同)

小智 23

如果您的每个数据项都有getter和setter,那么将数据设为私有就没有意义了.这就是为什么为每个数据项设置getter和setter是个坏主意的原因.考虑一下std :: string类 - 它(可能)有一个getter,size()函数,根本没有setter.

或者考虑一个BankAccount对象 - 我们是否应该 SetBalance()设置更改当前余额?不,大多数银行都不会感谢你实施这样的事情.相反,我们想要类似的东西ApplyTransaction( Transaction & tx ).

  • 尼尔,我认为这不是一个好建议.吸气剂和制定者是Qt物业系统的重要组成部分.没有它们你就不能拥有它们.此外,getter和setter可以执行其他必要的操作,例如锁定互斥锁,查询数据库,隐式共享,引用计数,延迟加载等. (3认同)
  • +1你希望通过"任务"/"操作"拥有一个界面,而不是getter/setter或公共数据. (2认同)
  • 你的第一句话让我失望了.即使你有吸气剂和固定剂,*仍然是使数据保密的一点.这似乎与你的大点正交,没有*getter和setter的私有数据*是可以的(甚至是可取的). (2认同)
  • @Bill你是对的 - 夸张效果.我的观点是get/set函数的一般不良. (2认同)

Jus*_*ner 10

Getters和Setter允许您将逻辑应用于私有成员的输入/输出,从而控制对数据的访问(对知道其OO术语的人进行封装).

公共变量使您的班级数据向公众开放,以进行不受控制和未经验证的操纵,这几乎总是不可取的.

你必须长期考虑这些事情.你现在可能没有验证(这就是为什么公共变量似乎是一个好主意)但是有可能它们会被添加到路上.提前添加它们离开了框架,因此对raod的重新分解更少,更不用说验证不会以这种方式破坏依赖代码).

但请记住,这并不意味着每个私有变量都需要自己的getter/setter.尼尔在他的银行业例子中提出了一个很好的观点,即Getters/Setters有时候没有意义.


Jer*_*fin 10

公开数据.在某种情况下(有点不太可能)事件,你有一天需要在"getter"或"setter"中使用逻辑,你可以将数据类型更改为重载的代理类operator=和/或operator T(其中T =您现在使用的任何类型)实现必要的逻辑.

编辑:控制对数据的访问构成封装的想法基本上是错误的.封装是关于隐藏实现的细节(通常!)控制对数据的访问.

封装是对抽象的补充:抽象处理对象的外部可见行为,而封装处理隐藏了如何实现该行为的细节.

使用getter或setter实际上降低了抽象级别并暴露了实现 - 它要求客户端代码知道这个特定的类实现了逻辑"数据"作为一对函数(getter和setter).正如我上面提到的那样使用代理提供了真正的封装 - 除了一个模糊的角落情况之外,它完全隐藏了这样一个事实,即逻辑上一块数据实际上是通过一对函数实现的.

当然,这需要保持在上下文中:对于某些类,"数据"根本不是一个好的抽象.一般来说,如果您可以提供更高级别的操作而不是数据,那么这是更可取的.尽管如此,有些类最可用的抽象是读取和写入数据 - 在这种情况下,(抽象的)数据应该像任何其他数据一样可见.获取或设置值的事实可能不仅仅涉及简单的位复制,而是应该向用户隐藏的实现细节.

  • @Jerry是对的.要么将数据公开,要么使类抽象化并将数据移动到子类.中间立场 - 私人数据w /公共吸气者和制定者 - 是黑客. (4认同)
  • 无论吸气者/制定者是否是好的设计,"有一个hackish workaround"不是一个争论,它是一个借口. (3认同)
  • @BlueRaja:胡说八道.看编辑.事实是,数据应该是完全不可见的(不是公共的,也不是getter或setter),否则它应该作为数据**可见**,在这种情况下,getter和setter暴露了错误的抽象. (3认同)
  • @Rob:简单的统计数据 - 即使在维持了几十年*的代码中,绝大多数的getter/setter仍然只是简单的传递,而这些传递根本没有完成任何有用的东西. (3认同)
  • "你可以将数据类型更改为代理类" - 是的,你可以,但它是一个可能比它的价值更麻烦的创可贴. (2认同)
  • @tloach:你显然不明白所提倡的是什么。拥有两个(或十几个)相同类型的成员变量并不会妨碍它的工作,甚至不会引起丝毫问题。 (2认同)

Igo*_*kon 5

如果您非常确定您的逻辑很简单,并且在读/写变量时您永远不需要做其他事情,那么最好保持数据公开.在C++的情况下,我更喜欢使用struct而不是class来强调数据是公共的这一事实.

但是,在访问数据成员时,您经常需要做一些其他事情,或者您希望以后自由地添加此逻辑.在这种情况下,吸气剂和制定者是个好主意.您的更改将对您的代码的客户端透明.

附加功能的一个简单示例 - 您可能希望每次访问变量时都记录调试字符串.


Blu*_*eft 5

除了封装问题(这是足够的理由)之外,只要在有getter/setter时设置/访问变量,就很容易设置断点.