35

これはかなり古いトピックです: セッターとゲッターは善か悪か?

ここでの私の質問は次のとおりです。C++ / D / Java のコンパイラはゲッターとセッターをインライン化しますか?

直接フィールド アクセスと比較して、ゲッター/セッターがパフォーマンス (関数呼び出し、スタック フレーム) にどの程度影響するか。それらを使用する他のすべての理由に加えて、それらが優れた OOP プラクティスであることに加えて、パフォーマンスに影響を与えることになっているかどうかを知りたいです。

4

10 に答える 10

26

場合によります。常に真実となる普遍的な答えはありません。

Javaでは、JITコンパイラは遅かれ早かれそれをインライン化するでしょう。私の知る限り、JVM JITコンパイラーは頻繁に使用されるコードのみを最適化するため、ゲッター/セッターが十分に頻繁に呼び出されるまで、最初は関数呼び出しのオーバーヘッドを確認できます。

C ++では、ほぼ確実にインライン化されます(最適化が有効になっていると仮定します)。ただし、おそらくそうではない場合が1つあります。

// 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がこの問題をどのように処理するかわかりません。

ひいては、これは、ゲッターが別の.dll / .soで定義されている場合、呼び出しをインライン化できないことも意味します。

いずれにせよ、些細なget / setterが必ずしも「優れたOOPプラクティス」であるとは限らず、「それらを使用する他のすべての理由」があるとは思いません。多くの人は、些細なゲット/セッターを1)悪いデザインの兆候であり、2)タイピングの無駄だと考えています。

個人的には、どちらの方法でも問題はありません。私にとって、「優れたOOPプラクティス」と見なされるには、定量化可能なプラスの効果が必要です。些細なget/setterには、わずかな利点と、わずかな欠点があります。そのため、私はそれらが良い習慣でも悪い習慣でもないと思います。本当にやりたいのなら、それはあなたができることです。

于 2009-07-10T15:08:44.950 に答える
9

D では、クラスのすべてのメソッド (構造体を除く) がデフォルトで仮想です。メソッドまたはクラス全体を final にすることで、メソッドを非仮想にすることができます。また、D にはプロパティ構文があり、何かをパブリック フィールドにしてから、後でソース レベルの互換性を損なうことなくゲッター/セッターに変更できます。したがって、DI では、特に理由がない限り、パブリック フィールドのみを使用することをお勧めします。ゲッターしか持たず、変数をクラス外から読み取り専用にするなど、何らかの理由で単純なゲッター/セッターを使用したい場合は、それらを 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で試しました。ゲッターとセッターの有無にかかわらず同じことです。結果:2つのバージョンの実行時間に大きな違いはありません。コードは次のとおりです。

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

まったく同じ質問を一度しました。

それに答えるために、私は2つの小さなプログラムをプログラムしました:

最初:

#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 (完全に最適化) を指定してそれらをアセンブラーにコンパイルし、2 つのファイルを比較しました。それらは同一でした。最適化がなければ、それらは異なっていました。

これはg ++の下にありました。したがって、あなたの質問に答えるには、コンパイラはゲッターとセッターを簡単に最適化します。

于 2009-07-10T23:26:41.863 に答える
4

そのソルトに値するJVM(またはコンパイラ)は、インライン化をサポートする必要があります。C++ では、コンパイラはゲッターとセッターをインライン化します。Java では、JVM は、「十分な」回数呼び出された後、実行時にそれらをインライン化します。Dについては知りません。

于 2009-07-10T14:52:12.760 に答える
4

クラスの予想される進化に応じて、get/setter はコードを混乱させる可能性がありますが、クライアント コードに影響を与えずに実装を拡張する柔軟性を提供します。

多くの場合、「データ コンテナー」として使用されるクラスに遭遇します。このクラスには、各メンバーに対してゲッターとセッターの両方があります。それはナンセンスです。メンバーを取得または設定するときに、「特別な」機能をトリガーする必要がない場合は、記述しないでください。

マシンの速度を考えると、追加の抽象化を作成する労力は、クライアントにより多くの費用がかかります。

ほとんどのコンパイラは単純な getter/setter を最適化できますが、それらを (C++ で) virtual と宣言するとすぐに、追加のルックアップを支払う必要があります。多くの場合、努力する価値があります。

于 2009-07-10T15:06:45.000 に答える
3

ここから引用Dプログラミング言語について

DMDは現在、仮想ゲッターとセッターを仮想化解除できないと思います。仮想呼び出し自体は少し遅くなりますが、インライン化もできないため、標準の最適化を連続して行うことはできません。したがって、属性へのそのようなアクセスが仮想呼び出しであり、これがコードの「ホット」部分で発生する場合、コードの速度が大幅に低下する可能性があります。(コードのホットでない部分で発生した場合、通常は効果がありません。そのため、Java Hot Spotは、非常に高速なプログラムを生成するためにすべてのコードを最適化する必要はありません)。

私はFritsvanBommelにLDCの仮想化解除機能を改善するように勧めました。

現在、LDCはいくつかの非常に単純な状況でそれを行うことができますが、ほとんどの場合、状況はDMDと比較して変わりません。最終的にはLLVMが改善されるため、この状況はそれ自体で改善される可能性があります。しかし、フロントエンドもこれについて何かをするかもしれません。

これは、このトピックに関するいくつかのドキュメントです。いくつかは古いもので、いくつかはより現代的なものです。

于 2009-07-10T23:47:15.160 に答える
3

場合によっては、特定のメンバーに getter が必要になります。ただし、クラスの各データ メンバーに getter と setter を提供することはお勧めできません。それを行うと、クラスのインターフェースが複雑になります。また、適切に処理しないと、セッターがリソースの所有権の問題を引き起こす可能性があります。

于 2009-07-10T15:41:34.080 に答える
2

C++ では、コンパイラの最適化が有効になっている場合、getter と setter が「インライン化」される場合があります。つまり、基になるメンバー データに直接アクセスする場合と同じマシン命令を使用して実装されます。

于 2009-07-10T14:52:52.400 に答える
1

Xtofl をエコーし​​ます。

ゲッターとセッターは時々役立つものの1つですが、「場合によっては役立つことが証明されている」から「常に使用する必要があり、使用しない場合はあなたは殺すべき異端者」。

get または set に副作用がある場合は、必ず getter または setter を使用してください。

しかしそうでなければ、getter と setter を書くことはコードを乱雑にするだけです。わずかなパフォーマンス ペナルティがあります。Java では、数回しか実行されない場合、パフォーマンス ペナルティは問題にならないはずです。また、頻繁に実行される場合は、ペナルティが軽減されるようにインライン化する必要があります。そのため、コードを読みにくくすることについてもっと心配します。

そして、これによってグローバル データの問題が解消されるという議論に、一瞬たりとも同意しないでください。グローバル データは不適切であり、避ける必要があります。しかし、メンバー フィールドをプライベートと宣言してからパブリックのゲッターとセッターを作成しても、問題は解決しません。

于 2009-07-10T17:15:39.910 に答える