getter和setter是否会影响C ++ / D / Java的性能?

时间:2009-07-10 14:47:42

标签: java c++ d

这是一个相当古老的话题:主持人和吸尘者是好还是坏?

我的问题是:做编译器 C ++ / D / Java 内联吸气剂和二传手?

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

10 个答案:

答案 0 :(得分:23)

这取决于。没有普遍的答案总是如此。

在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应该能够内联它。我不知道海湾合作委员会如何处理这个问题。

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

在任何情况下,我都不认为琐碎的获取/制定者必然是“良好的OOP实践”,或者“存在使用它们的所有其他原因”。很多人认为琐碎的获取/设置者1)是设计糟糕的标志,2)浪费打字。

就个人而言,这不是我对任何一种方式都有所了解的事情。对我而言,对于有资格作为“良好的OOP实践”的东西,它必须具有一些可量化的积极效果。琐碎的获取/制定者有一些边缘优势,有些也是微不足道的缺点。因此,我不认为他们是好的或坏的做法。如果你真的想要,它们就是你能做的事情。

答案 1 :(得分:9)

在D中,默认情况下,所有类的方法,但不是结构,都是虚拟的。您可以通过使方法或整个类最终来使方法非虚拟。此外,D具有属性语法,允许您创建公共字段,然后在不破坏源级兼容性的情况下将其更改为getter / setter。因此,在D中我建议只使用公共字段,除非你有充分的理由不这样做。如果你想出于某种原因使用琐碎的getter / setter,比如只有一个getter并且让这个变量只能从类外部读取,那就把它们作为最终的。


编辑:例如,行:

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

适用于两者

struct S
{
     int foo;
     int bar;
}

struct S
{
     void foo(int) { ... }
     int bar() { ... return something; }
}

答案 2 :(得分:6)

我有一次完全相同的问题。

回答问题,我编写了两个小程序:

第一个:

#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。

答案 3 :(得分: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());
}

}

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

答案 4 :(得分:4)

任何有价值的JVM(或编译器)都需要支持内联。在C ++中,编译器内联getter和setter。在Java中,JVM在被称为“足够”时间后在运行时内联它们。我不知道D。

答案 5 :(得分:4)

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

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

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

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

答案 6 :(得分:3)

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

答案 7 :(得分:3)

引自here关于D Programming Langiage

  

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

     

I have encouraged Frits van Bommel改善了LDC的虚拟化能力:

     

现在LDC能够在很少的非常简单的情况下做到这一点,但大多数情况下与DMD相比情况没有变化。最终LLVM将会改进,因此这种情况本身可以得到改善。但前端也可能对此有所帮助。

     

以下是有关此主题的一些文档,some oldersome more modern

答案 8 :(得分:2)

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

答案 9 :(得分:1)

我会回应Xtofl。

吸气剂和制定者是有时候有用的东西之一,但是你会得到这些教条主义者,他们会以某种方式从“在某些情况下证明有用”到“必须一直使用,如果你不使用它”你是一个应该被杀的异教徒“。

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

但如果没有,编写getter和setter只会使代码混乱。性能损失很小。在Java中,如果它只执行了几次,那么性能损失应该无关紧要,如果频繁执行,则应该内联,以便减轻惩罚。所以我更担心让代码更难阅读。

不要一分钟购买这个消除全球数据问题的论点。全球数据很糟糕,应该避免。但是将成员字段声明为私有然后创建公共getter和setter并不能解决问题。