将getter和setter设置为内联代码是一种好习惯吗?


76
public:
     inline int GetValue() const {
          return m_nValue;
     }
     inline void SetValue(int nNewValue) {
          this -> m_nValue = nNewValue;
     }

他们说,在Learn C ++上,它将运行得更快。因此,我认为在getter和setter上使用它会很棒。但是,也许有一些缺点吗?


1
谢谢大家!一般结论:不要这样做,编译器会照顾好它。
马丁·考特

1
这取决于。如果您在类定义中包含它们,则无需内联它们,因为默认情况下它们已经存在。如果要在单独的.cpp文件中执行该实现,则取决于链接器,该链接器可以像在著名平台上一样聪明,或者可以是一个愚蠢的链接器,它不会在鲜为人知的平台AFAIK上内联。
AraK 2010年

4
让我在下面给出的答案中加几句话。就我个人而言,我非常不喜欢用代码弄乱我的类声明,因为我认为这是(技术)文档的一部分。头文件中方法定义的自变量相同,尽管效果不那么糟糕。啊,最后:您真的需要吸气剂和吸气剂吗?:-)
mkluwe 2010年

2
@mkluwe +1我同意,getter和setter很少是良好实践的一部分。
daramarak 2010年

1
@daramarak:实际上,主要是二传手是不好的做法。
安德烈·卡隆

Answers:


66

在探查器明确告诉我没有内联会导致性能问题之前,我不会进行任何内联。

C ++编译器非常聪明,几乎可以肯定会自动为您内联这样的简单函数。通常,它比您更聪明,并且在确定应内联或不应内联的内容方面会做得更好。

我会避免考虑要插入或不插入什么并专注于解决方案。inline稍后添加关键字(这不是内联BTW的保证)非常容易做到,并且可以使用探查器轻松找到潜在位置。


47
如果将其实现放入单独的源文件中,则编译器无法内联某些东西。与给出的示例无关,但是值得一提。
Mark Ransom 2010年

22
实际上,GCC和VS都提供源文件之间的内联。
小狗

7
@Mark-这是正确的,因为在那个阶段它实际上是链接器,而不是编译器。
Edward Strange 2010年

2
@DeadMG,我从未听说过,也无法想象它将如何实现。有连结吗?
Mark Ransom

3
如果两个文件都在同一个项目中,则链接器可能会完成此任务。但是,如果您链​​接一个预编译的DLL,则它无法内联任何未明确包含在头文件中的内容。
mmmmmmmm 2010年

30

如果将它们写在定义中,则inline 默认情况下会考虑它们。

这意味着将允许它们在多个编译单元中使用(因为类定义本身通常会出现在多个编译单元中),而不是实际上它们内联。


4
如果编译器要内联它们…
mk12'2

5
inline如果在类定义中定义了函数,则C ++标准会考虑这些函数,这几乎与内联函数的编译器/链接器完全无关。
Mooing Duck 2012年

15

这是公共API中的错误做法。对这些功能的任何更改都需要重新编译所有客户端。

通常,具有吸气剂和吸气剂显示出较差的抽象,请不要这样做。如果您要不断访问另一个类中的原始数据,那么您可能需要重新安排您的类,而不是考虑您希望如何在一个类中操纵数据并提供适当的方法。


6
您能详细说明糟糕的抽象吗?您究竟是什么意思,以及如何反映此示例?m_nValue应该只是公开的吗?
stijn 2010年

7
@stijn:类应该是某种东西的抽象。它的实现无关紧要,对其进行的操作对其所代表的内容也应该有意义。应该有类不变式:关于该类始终是正确的语句。获取器和设置器公开的实现几乎与公共变量一样多。以我的经验,就类抽象而言,各个变量的值往往没有意义,因此您允许对该类进行与其抽象无关的操作。获取器和设置器使保持不变性变得更加困难。
David Thornley 2010年

66
教条式的废话。您认为不需要获取字符串的长度吗?更改UI按钮上的文本?获取鼠标的当前坐标?当然,对于任何模式,都可能滥用getter和setter。但是,作为一项总括规则,“不要这样做”是国际海事组织的不良建议。
user168715 2010年

3
我认为每个人的反应都过分敏感。显然,容器具有访问器(很好,通过引用等效):显然,它们是包装特定可修改值的目的之一。同样清楚的是,大多数类都不是容器。建议的问题“一般来说,不要这样做”是很难使用建议-对于任何一个好的吸气剂/设定者来说,回答是“我说不要一般,不要这样做”当它是一个好主意时就不要这样做”。因此,该建议恰好等同于“一般而言,仅在特定情况下才做一个好主意” ;-p
Steve Jessop 2010年

3
@Steve:也许我们可以提出一些更有用的东西。这是我的建议:不要在getter和setter中进行设计。设计功能对抽象很有用。例如,说点的X坐标或字符串的长度是有意义的。如果事实证明它们本质上是获取器和设置器,那很容易实现。
David Thornley 2010年

10

缺点:

  1. 编译器可以自由地忽略您。

  2. 对这些功能的任何更改都需要重新编译所有客户端。

  3. 一个好的编译器将在适当的时候内联非内联函数。


5

我还想补充一点,除非您每帧要执行数百万次获取/设置,否则是否内联这些都几乎无关紧要。老实说,这不值得失去睡眠。

另外,请记住,仅因为在声明+定义前面加上了“内联”一词,并不意味着编译器将内联您的代码。它使用各种启发式方法来确定是否有意义,这通常是速度与大小之间的经典折衷。但是,在VC ++中至少有一个蛮力的'__forceinline'关键字(我不确定在GCC中是什么),这使编译器很想不到。我真的不建议这样做,除了一旦您移植到其他体系结构上,它可能是不正确的。

尝试将所有函数定义放在实现文件中,并保留标头的纯声明(当然,除非您要进行模板元编程(STL / BOOST / etc),在这种情况下,标头中几乎所有内容都包含在内;)

人们喜欢内联的经典地方之一(至少在我来自的视频游戏中)是数学标头。十字/点积,向量长度,矩阵清除等通常放在标题中,我认为这是不必要的。9/10对性能没有影响,如果您需要进行紧密循环,例如通过某种矩阵转换大型矢量数组,则最好手动进行内联数学运算,甚至更好地对其进行编码特定于平台的汇编器。

哦,还有一点,如果您觉得确实需要一个类才能使数据多于代码,请考虑使用良好的旧结构,该结构不会带来OO抽象的负担,这就是它的用途。:)

抱歉,并不是说要继续做那么多,但我只是认为考虑现实世界的用例会有所帮助,而不必太过依赖固定的编译器设置(相信我,我去过那里;)

祝好运。

尚恩


1
此处的另一条评论:如果您强制内联,则冒着使循环过大并可能存在一些缓存问题的风险。对于现代CPU,性能问题可能会违反直觉。如果要强制内联,请在有或没有的情况下进行性能测试,并仅在有帮助时才保留强制内联。
David Thornley 2010年

3

通过将代码放在标头中,您可以公开内部类的工作原理。客户可能会看到这一点,并假设您的课堂如何工作。这会使以后在不破坏客户端代码的情况下更改类变得更加困难。


2

该代码将稍稍编译,您将失去封装。一切都取决于项目的规模及其性质。在大多数情况下,如果它们没有任何复杂的逻辑,可以使其内联。

顺便说一句,inline如果直接在类定义中实现,则可以跳过。


2

内联关键字对您而言毫无意义

编译器将在可能的情况下内联您的函数,而与关键字无关。

关键字内联影响链接而不是内联。这有点令人困惑,但请仔细阅读。

如果定义与调用位于不同的编译单元(基本上是预处理器之后的源文件)中,则仅在启用整个项目优化和链接时间代码生成的情况下才可以进行内联。启用它会大大增加链接时间(因为它实际上会重新编译链接器中的所有内容),但是显然可以提高性能。不确定在GCC和VS中默认情况下是打开还是关闭。



1

不需要,至少在进行此类优化时,开始信任编译器!
“但不总是”


1

我得说,我对此线程上的其他人似乎并不强烈反对这种做法。我同意,除了使用最频繁的案例外,内联带来的性能提升可以忽略不计。(是的,我已经在实践中遇到这样的情况。)如果我做这样的内联的,我这样做是为了方便,一般只为单行这样。在我的大多数用例中,如果我更改了它们,那么避免在客户端进行重新编译的需求并不是那么强烈。

是的,您可以删除 inline,因为实现的位置暗示它。

另外,我对这种针对访问者的热情感到有些惊讶。您几乎不能轻描淡写使用任何OO语言进行的类学习,毕竟它们是一种从接口抽象实现的有效技术,因此,将它们称为OO实践不佳有点受虐狂。这很好的建议不写存取乱射,但我也劝你不要在热情冲昏头脑,以消除它们。

纯粹主义者,接受这一点。:-)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.