35

这是一个相当古老的话题:setter 和 getter 是好还是坏?

我的问题是:C++/D/Java 中的编译器是否内联了 getter 和 setter?

与直接字段访问相比,getter/setter 在多大程度上影响性能(函数调用、堆栈帧)。除了使用它们的所有其他原因之外,我想知道它们是否应该影响性能,除了是一个好的 OOP 实践。

4

10 回答 10

26

这取决于。没有永远正确的普遍答案。

在 Java 中,JIT 编译器迟早会内联它。据我所知,JVM JIT 编译器仅优化大量使用的代码,因此您最初可以看到函数调用开销,直到 getter/setter 被足够频繁地调用。

在 C++ 中,它几乎肯定会被内联(假设启用了优化)。但是,有一种情况可能不会:

// foo.h
class Foo {
private:
  int bar_;

public:
  int bar(); // getter
};

// foo.cpp
#include "foo.h"

int Foo::bar(){
  return bar_;
}

如果类的用户看不到函数的定义(包括 foo.h,但看不到 foo.cpp),那么编译器可能无法内联函数调用。

如果启用链接时代码生成作为优化,MSVC 应该能够内联它。我不知道 GCC 如何处理这个问题。

通过扩展,这也意味着如果 getter 定义在不同的 .dll/.so 中,则不能内联调用。

无论如何,我认为微不足道的 get/setter 不一定是“良好的 OOP 实践”,或者“使用它们的所有其他原因”。很多人认为微不足道的 get/setter 1)是糟糕设计的标志,以及 2)浪费打字。

就个人而言,这不是我对任何一种方式都感到兴奋的事情。对我来说,要获得“良好的 OOP 实践”的资格,它必须具有一些可量化的积极影响。微不足道的 get/setter 有一些边际优势,还有一些同样微不足道的缺点。因此,我不认为它们是一个好的或坏的做法。如果您真的愿意,它们只是您可以做的事情。

于 2009-07-10T15:08:44.950 回答
9

在 D 中,类的所有方法,但不是结构,默认情况下都是虚拟的。您可以通过将方法或整个类设为 final 来使方法成为非虚拟方法。此外,D 具有属性语法,允许您将某些内容设为公共字段,然后稍后将其更改为 getter/setter,而不会破坏源代码级别的兼容性。因此,在 DI 中建议只使用公共字段,除非您有充分的理由不这样做。如果您出于某种原因想要使用琐碎的 getter/setter,例如只有一个 getter 并将变量从类外部设为只读,请将它们设为 final。


编辑:例如,行:

S s;
s.foo = s.bar + 1;

对双方都有效

struct S
{
     int foo;
     int bar;
}

struct S
{
     void foo(int) { ... }
     int bar() { ... return something; }
}
于 2009-07-10T15:41:14.267 回答
6

我在 Java 中尝试过:无论有没有 getter 和 setter,都是一样的。结果:两个版本的执行时间没有显着差异。这是代码:

class Person
{
    public int age;
    String name;
    public Person(String name,int age)
    {
        this.name=name;
        this.age=age;   
    }

}
class GetSetPerson
{
    private int age;
    String name;
    public GetSetPerson(String name,int age)
    {
        this.name=name;
        this.age=age;   
    }
    public void setAge(int newage)
    {
        age=newage;
    }
    public int getAge()
    {
    return age; 
    }
}
class Proba
{
//Math.hypot kb 10-szer lassabb, mint a Math.sqrt(x*xy*y)!!!

public static void main(String args[])
{
long startTime, endTime, time;
int i;int agevar;
//Person p1=new Person("Bob",21);
GetSetPerson p1=new GetSetPerson("Bob",21);
startTime=System.nanoTime();
/*
for (i=0;i<1000000000;i++)
     {
     p1.age++;

     }

*/    
for (i=0;i<1000000000;i++)
     {
     agevar=p1.getAge();
     agevar++;
     p1.setAge(agevar);
     }

endTime=System.nanoTime();
time=endTime-startTime;
System.out.println(""+time);
System.out.println(p1.name+"'s age now  is "+p1.getAge());
}

}

我知道,最后鲍勃比我大一点,但为此,应该没问题。

于 2012-04-15T19:27:38.480 回答
5

我曾经有完全相同的问题。

为了回答它,我编写了两个小程序:

首先:

#include <iostream>

class Test
{
     int a;
};

int main()
{
    Test var;

    var.a = 4;
    std::cout << var.a << std::endl;
    return 0;
}

第二:

#include <iostream>

class Test
{
     int a;
     int getA() { return a; }
     void setA(int a_) { a=a_; }
};

int main()
{
    Test var;

    var.setA(4);
    std::cout << var.getA() << std::endl;
    return 0;
}

我使用 -O3 (完全优化)将它们编译为汇编程序并比较了这两个文件。他们是相同的。如果没有优化,它们是不同的。

这是在 g++ 下。所以,回答你的问题:编译器很容易优化 getter 和 setter。

于 2009-07-10T23:26:41.863 回答
4

任何值得一提的 JVM(或编译器)都需要支持内联。在 C++ 中,编译器内联 getter 和 setter。在 Java 中,JVM 在调用“足够”次数后在运行时内联它们。我不知道D。

于 2009-07-10T14:52:12.760 回答
4

根据您的类的预期演变,get/setter 可能会使您的代码混乱,而不是让您灵活地扩展您的实现而不影响客户端代码。

我经常遇到用作“数据容器”的类,每个成员都有 getter 和 setter。那是胡说八道。如果您不希望在获取或设置成员时需要触发某些“特殊”功能,请不要编写它。

鉴于机器的速度,编写额外抽象的工作将花费您的客户更多的钱。

大多数编译器可以优化琐碎的 getter/setter,但是一旦您将它们声明为虚拟的(在 C++ 中),您就需要支付额外的查找费用 - 通常值得付出努力。

于 2009-07-10T15:06:45.000 回答
3

在某些情况下,某些成员需要吸气剂。但是,为类的每个数据成员提供 getter 和 setter 并不是一个好习惯。这样做会使类的界面复杂化。如果处理不当,setter 也会带来资源所有权问题。

于 2009-07-10T15:41:34.080 回答
3

从这里引用关于 D 编程语言

我认为 DMD 目前无法对虚拟 getter 和 setter 进行去虚拟化。虚拟调用本身有点慢,但它们也不允许内联,因此无法进行连续的标准优化。因此,如果对属性的这种访问是虚拟调用,并且这发生在代码的“热”部分,那么它可能会显着减慢您的代码。(如果它发生在代码的非热点部分,它通常没有影响。这就是为什么 Java Hot Spot 不需要优化所有代码来生成一个非常快速的程序)。

我鼓励Frits van Bommel 提高 LDC 的去虚拟化能力:

现在 LDC 能够在一些非常简单的情况下做到这一点,但与 DMD 相比,大多数情况下情况并没有改变。最终 LLVM 会有所改善,因此这种情况可以自行改善。但前端也可能对此有所作为。

以下是有关此主题的一些文档,一些较旧一些更现代

于 2009-07-10T23:47:15.160 回答
2

在 C++ 中,当启用编译器的优化时,getter 和 setter 可能会被“内联”:即,使用与直接访问底层成员数据相同的机器指令来实现。

于 2009-07-10T14:52:52.400 回答
1

我会回应 Xtofl。

getter 和 setter 是有时有用的东西之一,但你会得到这些教条主义的人,他们不知何故从“在某些情况下被证明有用”跳跃到“必须一直使用,如果你不使用它,你就是一个应该被杀的异端”。

如果您对 get 或 set 有副作用,请务必使用 getter 或 setter。

但如果不是这样,编写 getter 和 setter 只会使代码混乱。有一个小的性能损失。在 Java 中,如果它只执行几次,性能惩罚应该无关紧要,如果它经常执行,它应该被内联以减轻惩罚。所以我更担心使代码更难阅读。

并且不要相信这消除了全球数据问题的论点。全局数据不好,应该避免。但是将成员字段声明为私有然后创建公共 getter 和 setter 并不能解决问题。

于 2009-07-10T17:15:39.910 回答