公共数据成员vs Getters,Setters

时间:2010-06-04 19:08:48

标签: c++ setter getter

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

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

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

15 个答案:

答案 0 :(得分:70)

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

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

编辑:发表评论后,我做了另一个答案。

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

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

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

答案 1 :(得分:34)

不,它甚至不是一回事。

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


1.公共数据成员:

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


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

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


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

  • 提供对属性的读取和/或写入访问权限
  • 不公开数据对象在物理上存在的事实,更不用说物理存在于此类中(不允许创建指向该成员类型的指针到该数据的指针,或任何类型关于那件事的指针)
  • 不提供左值数据访问权限(不允许创建普通指针)

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

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

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

答案 2 :(得分:23)

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

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

答案 3 :(得分:10)

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

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

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

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

答案 4 :(得分:10)

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

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

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

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

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

答案 5 :(得分:5)

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

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

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

答案 6 :(得分:5)

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

答案 7 :(得分:4)

使用公共字段而不是getter和setter的原因包括:

  1. 没有非法的价值观。
  2. 客户需要对其进行编辑。
  3. 能够写出诸如object.X.Y = Z。
  4. 之类的东西
  5. 强烈承诺价值只是一个价值,并且没有与之相关的副作用(并且未来也不会有)。
  6. 根据您使用的软件类型,这些可能都是非常特殊的情况(如果您认为自己遇到了可能错误的情况),或者它们可能会一直发生。这真的取决于。

    (来自Ten Questions on Value-Based Programming。)

答案 8 :(得分:3)

在严格的实践基础上,我建议您首先将所有数据成员设为私有, AND 将其getter和setter设为私有。当您发现世界其他地方(即您的“(l)用户社区”)实际需要的内容时,您可以公开适当的获取者和/或设置者,或者编写适当控制的公共访问者。

另外(对于Neil的好处),在调试期间,当读取或写入特定数据成员时,有时可以方便地挂起调试打印和其他操作。有了getter和setter,这很容易。对于公共数据成员来说,后面是一个巨大的痛苦。

答案 9 :(得分:2)

我一直认为,大多数编程语言中的getter和setter都是故意冗长的,特别是让你三思而后行 - 为什么你的调用者需要知道你的类的内部工作应该是前面的问题你的思想。

答案 10 :(得分:2)

我认为使用getter和setter只是为了获取和设置值是没用的。公共成员与使用此类方法的私人成员之间没有区别。仅当您需要以某种方式控制值或者您认为它将来可能有用时才使用getter和setter(添加一些逻辑不会让您编辑剩余的代码)。

As a reference, read C++ guidelines (C.131)

答案 11 :(得分:1)

我建议您没有公共数据成员(POD结构除外)。我也不建议您为所有数据成员提供getter和setter。而是为您的类定义一个干净的公共接口。这可以包括获取和/或设置属性值的方法,并且这些属性可以实现为成员变量。但是不要为所有成员制作吸气剂和制定者。

您的想法是将界面与实现分开,允许您修改实现,而不需要类的用户更改其代码。如果您通过getter和setter公开所有内容,那么使用公共数据就没有任何改进。

答案 12 :(得分:1)

使用getter和setter将允许您修改为用户提供值的方式。

请考虑以下事项:

double premium;
double tax;

然后使用此premium值在整个地方编写代码以获得溢价:

double myPremium = class.premium;

您的规格刚刚改变,用户的立场需要premium + tax

您必须在代码中使用premium值的任何地方进行修改,并向其添加tax

相反,如果你实现了它:

double premium;
double tax;

double GetPremium(){return premium;};

您的所有代码都将使用GetPremium(),而您的tax更改将是一行:

double premium;
double tax;

double GetPremium(){return premium + tax;};

答案 13 :(得分:0)

返回值也会影响getter和setter的使用。获取变量的值或访问私有数据成员变量是不同的。按值保持完整性,按引用或按指针而不是那么多。

答案 14 :(得分:0)

Getters和Setter主要存在,以便我们可以控制成员的获取方式以及如何设置成员。 Getters和setter不仅仅作为访问特定成员的方式存在,而是为了确保在我们尝试设置成员之前,它可能满足某些条件,或者如果我们获取它,我们可以控制我们返回该副本非原始类型的成员。总的来说,当你想要管理数据成员如何与之交互时,你应该尝试使用g / s&#crs,而没有它们会导致成员以一种特殊的方式使用。